diff --git a/CMake/PackageDepends/MITK_GDCM_Config.cmake b/CMake/PackageDepends/MITK_GDCM_Config.cmake index 0e26d2dd63..3a85869601 100644 --- a/CMake/PackageDepends/MITK_GDCM_Config.cmake +++ b/CMake/PackageDepends/MITK_GDCM_Config.cmake @@ -1,2 +1,10 @@ -list(APPEND ALL_INCLUDE_DIRECTORIES ${GDCM_INCLUDE_DIRS}) -list(APPEND ALL_LIBRARIES ${GDCM_LIBRARIES}) +foreach(gdcm_component ${GDCM_REQUIRED_COMPONENTS_BY_MODULE}) + if(NOT gdcm_component MATCHES "^gdcm") + set(gdcm_component "gdcm${gdcm_component}") + endif() + list(APPEND _gdcm_required_components_by_module ${gdcm_component}) +endforeach() + +find_package(GDCM COMPONENTS ${_gdcm_required_components_by_module} REQUIRED) + +list(APPEND ALL_LIBRARIES ${_gdcm_required_components_by_module}) diff --git a/CMakeExternals/ITK-4.13.3.patch b/CMakeExternals/ITK-4.13.3.patch deleted file mode 100644 index a21a50b384..0000000000 --- a/CMakeExternals/ITK-4.13.3.patch +++ /dev/null @@ -1,53 +0,0 @@ -diff --git a/Modules/Core/Common/include/itkBoundingBox.h b/Modules/Core/Common/include/itkBoundingBox.h -index 36b03aaa7f..64205acfab 100644 ---- a/Modules/Core/Common/include/itkBoundingBox.h -+++ b/Modules/Core/Common/include/itkBoundingBox.h -@@ -87,7 +87,7 @@ public: - itkTypeMacro(BoundingBox, Object); - - /** Method for creation through the object factory. */ -- itkNewMacro(Self); -+ itkFactorylessNewMacro(Self); - - /** Hold on to the type information specified by the template parameters. */ - typedef TPointIdentifier PointIdentifier; -diff --git a/Modules/Core/Common/include/itkVectorContainer.h b/Modules/Core/Common/include/itkVectorContainer.h -index 72293c4ce8..e6366c66b5 100644 ---- a/Modules/Core/Common/include/itkVectorContainer.h -+++ b/Modules/Core/Common/include/itkVectorContainer.h -@@ -92,7 +92,7 @@ public: - typedef VectorType STLContainerType; - - /** Method for creation through the object factory. */ -- itkNewMacro(Self); -+ itkFactorylessNewMacro(Self); - - /** Standard part of every itk Object. */ - itkTypeMacro(VectorContainer, Object); -diff --git a/Modules/Core/Transform/include/itkScalableAffineTransform.h b/Modules/Core/Transform/include/itkScalableAffineTransform.h -index 4e830476a2..b5efeb95a4 100644 ---- a/Modules/Core/Transform/include/itkScalableAffineTransform.h -+++ b/Modules/Core/Transform/include/itkScalableAffineTransform.h -@@ -47,7 +47,8 @@ public: - itkTypeMacro(ScalableAffineTransform, AffineTransform); - - /** New macro for creation of through a Smart Pointer */ -- itkNewMacro(Self); -+ itkFactorylessNewMacro(Self); -+ itkCloneMacro(Self); - - /** Dimension of the domain space. */ - itkStaticConstMacro(InputSpaceDimension, unsigned int, NDimensions); -diff --git a/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h b/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h -index e75a220d4d..90d5bf1218 100644 ---- a/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h -+++ b/Modules/ThirdParty/OpenJPEG/src/openjpeg/opj_includes.h -@@ -88,7 +88,7 @@ Most compilers implement their own version of this keyword ... - #endif - - /* MSVC and Borland C do not have lrintf */ --#if defined(_MSC_VER) || defined(__BORLANDC__) -+#if (defined(_MSC_VER) && _MSC_VER < 1928) || defined(__BORLANDC__) - - /* MSVC 64bits doesn't support _asm */ - #if !defined(_WIN64) diff --git a/CMakeExternals/ITK-5.2.1.patch b/CMakeExternals/ITK-5.2.1.patch new file mode 100644 index 0000000000..8ad1cb5184 --- /dev/null +++ b/CMakeExternals/ITK-5.2.1.patch @@ -0,0 +1,35 @@ +diff --git a/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h b/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h +index 1570a182..96a70f17 100644 +--- a/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h ++++ b/Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h +@@ -168,7 +168,7 @@ public: + const std::vector & dim, + const char * fourCC, + unsigned int nChannels, +- IOComponentType componentType); ++ IOComponentEnum componentType); + + protected: + OpenCVVideoIO(); +diff --git a/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx b/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx +index 7d7db3b8..8ce71c8f 100644 +--- a/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx ++++ b/Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx +@@ -436,7 +436,7 @@ OpenCVVideoIO::SetWriterParameters(TemporalRatioType fps, + const std::vector & dim, + const char * fourCC, + unsigned int nChannels, +- IOComponentType componentType) ++ IOComponentEnum componentType) + { + if (this->m_ReaderOpen || this->m_WriterOpen) + { +@@ -444,7 +444,7 @@ OpenCVVideoIO::SetWriterParameters(TemporalRatioType fps, + } + + // Make sure componentType is acceptable (right now we only support char) +- if (componentType != UCHAR) ++ if (componentType != IOComponentEnum::UCHAR) + { + itkExceptionMacro("OpenCV IO only supports writing video with pixels of UCHAR"); + } diff --git a/CMakeExternals/ITK.cmake b/CMakeExternals/ITK.cmake index 3869dd614a..fbf427ad66 100644 --- a/CMakeExternals/ITK.cmake +++ b/CMakeExternals/ITK.cmake @@ -1,87 +1,88 @@ #----------------------------------------------------------------------------- # ITK #----------------------------------------------------------------------------- # Sanity checks if(DEFINED ITK_DIR AND NOT EXISTS ${ITK_DIR}) message(FATAL_ERROR "ITK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj ITK) set(proj_DEPENDENCIES GDCM) if(MITK_USE_OpenCV) list(APPEND proj_DEPENDENCIES OpenCV) endif() if(MITK_USE_HDF5) list(APPEND proj_DEPENDENCIES HDF5) endif() set(ITK_DEPENDS ${proj}) if(NOT DEFINED ITK_DIR) set(additional_cmake_args -DUSE_WRAP_ITK:BOOL=OFF) + list(APPEND additional_cmake_args + -DITKV4_COMPATIBILITY:BOOL=OFF + -DITK_LEGACY_REMOVE:BOOL=ON + ) + if(MITK_USE_OpenCV) list(APPEND additional_cmake_args -DModule_ITKVideoBridgeOpenCV:BOOL=ON -DOpenCV_DIR:PATH=${OpenCV_DIR} ) endif() # Keep the behaviour of ITK 4.3 which by default turned on ITK Review # see MITK bug #17338 list(APPEND additional_cmake_args -DModule_ITKReview:BOOL=ON -DModule_ITKOpenJPEG:BOOL=ON # for 4.7, the OpenJPEG is needed by review but the variable must be set -DModule_IsotropicWavelets:BOOL=ON ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() mitk_query_custom_ep_vars() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} UPDATE_COMMAND "" - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/InsightToolkit-4.13.3.tar.gz - URL_MD5 d1c10c8288b47577d718a71190444815 - PATCH_COMMAND - # 2021/03/26: Only the patch file changed since the last snapshot. - # The only purpose of this comment is to change this .cmake file - # to make our build system aware of a change. - ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/ITK-4.13.3.patch + URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/InsightToolkit-5.2.1.tar.gz + URL_MD5 48c1fe49f75fdaa91b31bbf9dda01a42 + PATCH_COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/ITK-5.2.1.patch CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} -DBUILD_EXAMPLES:BOOL=OFF -DITK_USE_SYSTEM_GDCM:BOOL=ON -DGDCM_DIR:PATH=${GDCM_DIR} -DITK_USE_SYSTEM_HDF5:BOOL=ON -DHDF5_DIR:PATH=${HDF5_DIR} ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) set(ITK_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/CMakeExternals/MatchPoint.cmake b/CMakeExternals/MatchPoint.cmake index ceba801875..dcda2ee574 100644 --- a/CMakeExternals/MatchPoint.cmake +++ b/CMakeExternals/MatchPoint.cmake @@ -1,69 +1,69 @@ #----------------------------------------------------------------------------- # MatchPoint #----------------------------------------------------------------------------- if(MITK_USE_MatchPoint) set(MatchPoint_SOURCE_DIR "" CACHE PATH "Location of the MatchPoint source directory") mark_as_advanced(MatchPoint_SOURCE_DIR) # Sanity checks if(DEFINED MatchPoint_DIR AND NOT EXISTS ${MatchPoint_DIR}) message(FATAL_ERROR "MatchPoint_DIR variable is defined but corresponds to non-existing directory") endif() if(NOT MatchPoint_DIR AND MatchPoint_SOURCE_DIR AND NOT EXISTS ${MatchPoint_SOURCE_DIR}) message(FATAL_ERROR "MatchPoint_SOURCE_DIR variable is defined but corresponds to non-existing directory") endif() set(proj MatchPoint) set(proj_DEPENDENCIES ITK) set(MatchPoint_DEPENDS ${proj}) if(NOT MatchPoint_DIR) if(MatchPoint_SOURCE_DIR) set(download_step SOURCE_DIR ${MatchPoint_SOURCE_DIR}) else() set(download_step - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/MatchPoint_rev_f2a64255.tar.gz - URL_MD5 4407836f85e6382e65c9d3a4bd79c370 + URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/MatchPoint_rev_f7699d1e.tar.gz + URL_MD5 8a24fbdccd6f18f158f49a0ad4fa3faa ) endif() string(REPLACE "-DBOOST_ALL_DYN_LINK" "" modified_ep_common_args "${ep_common_args}") ExternalProject_Add(${proj} ${download_step} # INSTALL_COMMAND "${CMAKE_COMMAND} -P cmake_install.cmake" CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${modified_ep_common_args} ${additional_cmake_args} -DBUILD_TESTING:BOOL=OFF -DITK_DIR:PATH=${ITK_DIR} #/src/ITK-build -DMAP_USE_SYSTEM_GDCM:BOOL=ON -DMAP_USE_SYSTEM_HDF5:BOOL=ON -DMAP_DISABLE_ITK_IO_FACTORY_AUTO_REGISTER:BOOL=ON -DMAP_WRAP_Plastimatch:BOOL=ON -DMAP_BUILD_Ontology:BOOL=ON -DMAP_BUILD_Ontology_simple:BOOL=ON -DGDCM_DIR:PATH=${GDCM_DIR} -DHDF5_DIR:PATH=${HDF5_DIR} CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) ExternalProject_Get_Property(${proj} binary_dir) set(${proj}_DIR ${binary_dir}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif(MITK_USE_MatchPoint) diff --git a/CMakeLists.txt b/CMakeLists.txt index d77d4c55b2..9b0253ccb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1443 +1,1444 @@ #[[ When increasing the minimum required version, check if Boost_ADDITIONAL_VERSIONS in CMake/PackageDepends/MITK_Boost_Config.cmake can be removed. See the first long comment in CMakeExternals/Boost.cmake for details. ]] set(MITK_CMAKE_MINIMUM_REQUIRED_VERSION 3.18) cmake_minimum_required(VERSION ${MITK_CMAKE_MINIMUM_REQUIRED_VERSION}) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19 AND CMAKE_VERSION VERSION_LESS 3.19.2) message(FATAL_ERROR "\ CMake v${CMAKE_VERSION} is defective [1]. \ Please either downgrade to v3.18 or upgrade to at least v3.19.2.\n\ [1] https://gitlab.kitware.com/cmake/cmake/-/issues/21529") endif() #----------------------------------------------------------------------------- # Policies #----------------------------------------------------------------------------- #[[ T28060 https://cmake.org/cmake/help/v3.18/policy/CMP0091.html https://cmake.org/cmake/help/v3.18/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html We pass CMP0091 to all external projects as command-line argument: -DCMAKE_POLICY_DEFAULT_CMP0091:STRING=OLD ]] cmake_policy(SET CMP0091 OLD) #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON) if(MITK_USE_SUPERBUILD) project(MITK-superbuild) set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR}) else() project(MITK VERSION 2021.10.99) include_directories(SYSTEM ${MITK_SUPERBUILD_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # MITK Extension Feature #----------------------------------------------------------------------------- set(MITK_EXTENSION_DIRS "" CACHE STRING "") unset(MITK_ABSOLUTE_EXTENSION_DIRS) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) get_filename_component(MITK_ABSOLUTE_EXTENSION_DIR "${MITK_EXTENSION_DIR}" ABSOLUTE) list(APPEND MITK_ABSOLUTE_EXTENSION_DIRS "${MITK_ABSOLUTE_EXTENSION_DIR}") endforeach() set(MITK_DIR_PLUS_EXTENSION_DIRS "${MITK_SOURCE_DIR}" ${MITK_ABSOLUTE_EXTENSION_DIRS}) #----------------------------------------------------------------------------- # Update CMake module path #----------------------------------------------------------------------------- set(MITK_CMAKE_DIR ${MITK_SOURCE_DIR}/CMake) set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake") if(EXISTS "${MITK_CMAKE_EXTENSION_DIR}") list(APPEND CMAKE_MODULE_PATH "${MITK_CMAKE_EXTENSION_DIR}") endif() endforeach() #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- # Standard CMake macros include(FeatureSummary) include(CTest) include(CMakeParseArguments) include(FindPackageHandleStandardArgs) # MITK macros include(mitkFunctionGetGccVersion) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkMacroEmptyExternalProject) include(mitkFunctionEnableBuildConfiguration) include(mitkFunctionWhitelists) include(mitkFunctionAddExternalProject) include(mitkFunctionAddLibrarySearchPaths) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #----------------------------------------------------------------------------- # Check miminum macOS version #----------------------------------------------------------------------------- # The minimum supported macOS version is 10.14. If you use a version less than # 10.14, there is no guarantee that the build still works. if(APPLE) exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE macos_version) if (macos_version VERSION_LESS "10.14") message(WARNING "Detected macOS version \"${macos_version}\" is not supported anymore. Minimum required macOS version is at least 10.14.") endif() if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.14) message(WARNING "Detected macOS deployment target \"${CMAKE_OSX_DEPLOYMENT_TARGET}\" is not supported anymore. Minimum required macOS version is at least 10.14.") endif() endif() #----------------------------------------------------------------------------- # Check miminum compiler versions #----------------------------------------------------------------------------- if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # require at least gcc 4.9 as provided by ppa:ubuntu-toolchain-r/test for Ubuntu 14.04 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) message(FATAL_ERROR "GCC version must be at least 4.9 If you are using Ubuntu 14.04, you can easily install gcc and g++ 4.9 (or any later version available) in addition to your version ${CMAKE_CXX_COMPILER_VERSION}: sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install gcc-4.9 g++-4.9 Make sure to explicitly specify these compilers when configuring MITK: CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc-4.9 CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++-4.9 For more information on the proposed PPA see the Toolchain Updates section of https://wiki.ubuntu.com/ToolChain.") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # require at least clang 3.4 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4) message(FATAL_ERROR "Clang version must be at least 3.4") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") # require at least clang 5.0 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) message(FATAL_ERROR "Apple Clang version must be at least 5.0") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # require at least Visual Studio 2017 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) message(FATAL_ERROR "Microsoft Visual Studio 2017 or newer required") endif() else() message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang (Linux or Apple), GCC and MSVC.") endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) else() set(GCC_VERSION 0) endif() set(MITK_CXX_STANDARD 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) mark_as_advanced( MITK_XVFB_TESTING MITK_FAST_TESTING MITK_BUILD_ALL_APPS ) #----------------------------------------------------------------------------- # Set UI testing flags #----------------------------------------------------------------------------- if(MITK_XVFB_TESTING) set(MITK_XVFB_TESTING_COMMAND "xvfb-run" "--auto-servernum" CACHE STRING "Command and options to test through Xvfb") mark_as_advanced(MITK_XVFB_TESTING_COMMAND) endif(MITK_XVFB_TESTING) # ----------------------------------------- # Other options set(MITK_CUSTOM_REVISION_DESC "" CACHE STRING "Override MITK revision description") mark_as_advanced(MITK_CUSTOM_REVISION_DESC) set_property(GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS "") include(CMakeExternals/ExternalProjectList.cmake) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMakeExternals") if(EXISTS "${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") include("${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") endif() endforeach() # ----------------------------------------- # Other MITK_USE_* options not related to # external projects build via the # MITK superbuild option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF) option(MITK_USE_OpenMP "Use OpenMP" OFF) option(MITK_USE_Python3 "Use Python 3" OFF) #----------------------------------------------------------------------------- # Build configurations #----------------------------------------------------------------------------- set(_buildConfigs "Custom") file(GLOB _buildConfigFiles CMake/BuildConfigurations/*.cmake) foreach(_buildConfigFile ${_buildConfigFiles}) get_filename_component(_buildConfigFile ${_buildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_buildConfigFile}) endforeach() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) file(GLOB _extBuildConfigFiles "${MITK_EXTENSION_DIR}/CMake/BuildConfigurations/*.cmake") foreach(_extBuildConfigFile ${_extBuildConfigFiles}) get_filename_component(_extBuildConfigFile "${_extBuildConfigFile}" NAME_WE) list(APPEND _buildConfigs "${_extBuildConfigFile}") endforeach() list(REMOVE_DUPLICATES _buildConfigs) endforeach() set(MITK_BUILD_CONFIGURATION "Custom" CACHE STRING "Use pre-defined MITK configurations") set_property(CACHE MITK_BUILD_CONFIGURATION PROPERTY STRINGS ${_buildConfigs}) mitkFunctionEnableBuildConfiguration() mitkFunctionCreateWhitelistPaths(MITK) mitkFunctionFindWhitelists(MITK) # ----------------------------------------- # Qt version related variables option(MITK_USE_Qt5 "Use Qt 5 library" ON) if(MITK_USE_Qt5) if(WIN32) set(MITK_QT5_MINIMUM_VERSION 5.12.9) else() set(MITK_QT5_MINIMUM_VERSION 5.12) endif() set(MITK_QT5_COMPONENTS Concurrent OpenGL PrintSupport Script Sql Svg Widgets Xml XmlPatterns WebEngineWidgets UiTools Help LinguistTools) if(APPLE) list(APPEND MITK_QT5_COMPONENTS DBus) elseif(UNIX) list(APPEND MITK_QT5_COMPONENTS X11Extras) endif() # Hint at default install locations of Qt if(NOT Qt5_DIR) if(MSVC) set(_dir_candidates "C:/Qt") if(CMAKE_GENERATOR MATCHES "^Visual Studio [0-9]+ ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") elseif(CMAKE_GENERATOR MATCHES "Ninja") include(mitkFunctionGetMSVCVersion) mitkFunctionGetMSVCVersion() if(VISUAL_STUDIO_PRODUCT_NAME MATCHES "^Visual Studio ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") endif() endif() if(_compilers MATCHES "[0-9]+") if (CMAKE_MATCH_0 EQUAL 2022) list(APPEND _compilers "msvc2019" "msvc2017") # Binary compatible elseif (CMAKE_MATCH_0 EQUAL 2019) list(APPEND _compilers "msvc2017") # Binary compatible endif() endif() else() set(_dir_candidates ~/Qt) if(APPLE) set(_compilers clang) else() list(APPEND _dir_candidates /opt/Qt) set(_compilers gcc) endif() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) foreach(_compiler ${_compilers}) list(APPEND _compilers64 "${_compiler}_64") endforeach() set(_compilers ${_compilers64}) endif() foreach(_dir_candidate ${_dir_candidates}) get_filename_component(_dir_candidate ${_dir_candidate} REALPATH) foreach(_compiler ${_compilers}) set(_glob_expression "${_dir_candidate}/5.*/${_compiler}") file(GLOB _hints ${_glob_expression}) list(SORT _hints) list(APPEND MITK_QT5_HINTS ${_hints}) endforeach() endforeach() endif() find_package(Qt5 ${MITK_QT5_MINIMUM_VERSION} COMPONENTS ${MITK_QT5_COMPONENTS} REQUIRED HINTS ${MITK_QT5_HINTS}) endif() # ----------------------------------------- # Custom dependency logic if(WIN32 AND Qt5_DIR) set(_dir_candidate "${Qt5_DIR}/../../../../../Tools/OpenSSL/Win_x64") get_filename_component(_dir_candidate ${_dir_candidate} ABSOLUTE) if(EXISTS "${_dir_candidate}") set(OPENSSL_ROOT_DIR "${_dir_candidate}") endif() endif() find_package(OpenSSL) option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") if(MITK_USE_cpprestsdk) if(NOT OpenSSL_FOUND) set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK).\n") if(UNIX) if(APPLE) set(openssl_message "${openssl_message}Please install it using your favorite package management " "system (i.e. Homebrew or MacPorts).\n") else() set(openssl_message "${openssl_message}Please install the dev package of OpenSSL (i.e. libssl-dev).\n") endif() else() set(openssl_message "${openssl_message}Please either install Win32 OpenSSL:\n" " https://slproweb.com/products/Win32OpenSSL.html\n" "Or use the Qt Maintenance tool to install:\n" " Developer and Designer Tools > OpenSSL Toolkit > OpenSSL 64-bit binaries\n") endif() set(openssl_message "${openssl_message}If it still cannot be found, you can hint CMake to find OpenSSL by " "adding/setting the OPENSSL_ROOT_DIR variable to the root directory of an " "OpenSSL installation. Make sure to clear variables of partly found " "versions of OpenSSL before, or they will be mixed up.") message(FATAL_ERROR ${openssl_message}) endif() list(APPEND MITK_USE_Boost_LIBRARIES date_time regex system) if(UNIX) list(APPEND MITK_USE_Boost_LIBRARIES atomic chrono filesystem random thread) endif() list(REMOVE_DUPLICATES MITK_USE_Boost_LIBRARIES) set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "A semi-colon separated list of required Boost libraries" FORCE) endif() if(MITK_USE_Python3) set(MITK_USE_ZLIB ON CACHE BOOL "" FORCE) if(APPLE AND CMAKE_FRAMEWORK_PATH AND CMAKE_FRAMEWORK_PATH MATCHES "python3\\.?([0-9]+)") find_package(Python3 3.${CMAKE_MATCH_1} EXACT REQUIRED COMPONENTS Interpreter Development NumPy) else() find_package(Python3 REQUIRED COMPONENTS Interpreter Development NumPy) endif() if(WIN32) string(REPLACE "\\" "/" Python3_STDARCH "${Python3_STDARCH}") string(REPLACE "\\" "/" Python3_STDLIB "${Python3_STDLIB}") string(REPLACE "\\" "/" Python3_SITELIB "${Python3_SITELIB}") endif() endif() if(BUILD_TESTING AND NOT MITK_USE_CppUnit) message("> Forcing MITK_USE_CppUnit to ON because BUILD_TESTING=ON") set(MITK_USE_CppUnit ON CACHE BOOL "Use CppUnit for unit tests" FORCE) endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("> Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() #----------------------------------------------------------------------------- # Pixel type multiplexing #----------------------------------------------------------------------------- # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES) set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) string(REPLACE "," ";" _integral_types ${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES}) string(REPLACE "," ";" _floating_types ${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES}) foreach(_scalar_type ${_integral_types} ${_floating_types}) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}itk::VariableLengthVector<${_scalar_type}>,") endforeach() string(LENGTH "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" _length) math(EXPR _length "${_length} - 1") string(SUBSTRING "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" 0 ${_length} MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES ${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES} CACHE STRING "List of vector pixel types used in AccessByItk and InstantiateAccessFunction macros for itk::VectorImage types" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() find_package(Git REQUIRED) #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") # Print configuration summary message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL) return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # Organize MITK targets in folders #----------------------------------------------------------------------------- set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(MITK_ROOT_FOLDER "MITK" CACHE STRING "") mark_as_advanced(MITK_ROOT_FOLDER) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(WriteBasicConfigVersionFile) include(CheckCXXSourceCompiles) include(GenerateExportHeader) include(mitkFunctionAddCustomModuleTest) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionCompileSnippets) include(mitkFunctionConfigureVisualStudioUserProjectFile) include(mitkFunctionCreateBlueBerryApplication) include(mitkFunctionCreateCommandLineApp) include(mitkFunctionCreateModule) include(mitkFunctionCreatePlugin) include(mitkFunctionCreateProvisioningFile) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionGetVersion) include(mitkFunctionGetVersionDescription) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionInstallCTKPlugin) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallThirdPartyCTKPlugins) include(mitkFunctionOrganizeSources) include(mitkFunctionUseModules) if( ${MITK_USE_MatchPoint} ) include(mitkFunctionCreateMatchPointDeployedAlgorithm) endif() include(mitkMacroConfigureItkPixelTypes) include(mitkMacroCreateExecutable) include(mitkMacroCreateModuleTests) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) include(mitkMacroGetPMDPlatformString) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroMultiplexPicType) # Deprecated include(mitkMacroCreateCTKPlugin) #----------------------------------------------------------------------------- # Global CMake variables #----------------------------------------------------------------------------- # 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) #----------------------------------------------------------------------------- if(OpenSSL_FOUND AND WIN32) set(MITK_OPENSSL_SSL_DLL "" CACHE FILEPATH "") set(MITK_OPENSSL_CRYPTO_DLL "" CACHE FILEPATH "") if(MITK_OPENSSL_SSL_DLL AND EXISTS "${MITK_OPENSSL_SSL_DLL}" AND MITK_OPENSSL_CRYPTO_DLL AND EXISTS "${MITK_OPENSSL_CRYPTO_DLL}") foreach(config_type ${CMAKE_CONFIGURATION_TYPES}) execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${MITK_BINARY_DIR}/bin/${config_type}") configure_file("${MITK_OPENSSL_SSL_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY) configure_file("${MITK_OPENSSL_CRYPTO_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY) endforeach() MITK_INSTALL(FILES "${MITK_OPENSSL_SSL_DLL}" "${MITK_OPENSSL_CRYPTO_DLL}" ) endif() endif() # Look for optional Doxygen package find_package(Doxygen) option(BLUEBERRY_DEBUG_SMARTPOINTER "Enable code for debugging smart pointers" OFF) mark_as_advanced(BLUEBERRY_DEBUG_SMARTPOINTER) # Ask the user to show the console window for applications option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) if(NOT MITK_FAST_TESTING) if(MITK_CTEST_SCRIPT_MODE STREQUAL "Continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "Experimental") set(MITK_FAST_TESTING ON) endif() endif() if(NOT UNIX) set(MITK_WIN32_FORCE_STATIC "STATIC" CACHE INTERNAL "Use this variable to always build static libraries on non-unix platforms") endif() if(MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() # Configure pixel types used for ITK image access multiplexing mitkMacroConfigureItkPixelTypes() # Configure module naming conventions set(MITK_MODULE_NAME_REGEX_MATCH "^[A-Z].*$") set(MITK_MODULE_NAME_REGEX_NOT_MATCH "^[Mm][Ii][Tt][Kk].*$") set(MITK_DEFAULT_MODULE_NAME_PREFIX "Mitk") set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) set(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME 1) #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK) # MITK_VERSION set(MITK_VERSION_STRING "${MITK_VERSION_MAJOR}.${MITK_VERSION_MINOR}.${MITK_VERSION_PATCH}") if(MITK_VERSION_PATCH STREQUAL "99") set(MITK_VERSION_STRING "${MITK_VERSION_STRING}-${MITK_REVISION_SHORTID}") endif() #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on macOS all BlueBerry plugins get copied into every # application bundle (.app directory) specified here if(MITK_USE_BLUEBERRY AND APPLE) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} Mitk${app_name}) endif() endforeach() endif() endforeach() endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}") set(MITK_C_FLAGS_DEBUG ) set(MITK_C_FLAGS_RELEASE ) set(MITK_CXX_FLAGS "${COVERAGE_CXX_FLAGS} ${MITK_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-deprecated-copy -Wno-array-bounds - + -Wno-cast-function-type + -Wno-maybe-uninitialized -fdiagnostics-show-option ) mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS) endforeach() endif() if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS) endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS) set(MITK_CXX_FLAGS_RELEASE "-U_FORTIFY_SOURCES -D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}") endif() set(MITK_MODULE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) set(MITK_EXE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PACKAGE_DEPENDS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake/PackageDepends") if(EXISTS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") list(APPEND MODULES_PACKAGE_DEPENDS_DIRS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") endif() endforeach() if(NOT MITK_USE_SYSTEM_Boost) set(Boost_NO_SYSTEM_PATHS 1) endif() set(Boost_USE_MULTITHREADED 1) set(Boost_USE_STATIC_LIBS 0) set(Boost_USE_STATIC_RUNTIME 0) set(Boost_ADDITIONAL_VERSIONS 1.74 1.74.0) # We need this later for a DCMTK workaround set(_dcmtk_dir_orig ${DCMTK_DIR}) # This property is populated at the top half of this file get_property(MITK_EXTERNAL_PROJECTS GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(MITK_USE_${ep} AND _package) if(_components) find_package(${_package} COMPONENTS ${_components} REQUIRED CONFIG) else() # Prefer config mode first because it finds external # Config.cmake files pointed at by _DIR variables. # Otherwise, existing Find.cmake files could fail. if(DEFINED ${_package}_DIR) #we store the information because it will be overwritten by find_package #and would get lost for all EPs that use on Find.cmake instead of config #files. set(_temp_EP_${_package}_dir ${${_package}_DIR}) endif(DEFINED ${_package}_DIR) find_package(${_package} QUIET CONFIG) string(TOUPPER "${_package}" _package_uc) if(NOT (${_package}_FOUND OR ${_package_uc}_FOUND)) if(DEFINED _temp_EP_${_package}_dir) set(${_package}_DIR ${_temp_EP_${_package}_dir} CACHE PATH "externaly set dir of the package ${_package}" FORCE) endif(DEFINED _temp_EP_${_package}_dir) find_package(${_package} REQUIRED) endif() endif() endif() endforeach() # Ensure that the MITK CMake module path comes first set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR} ${CMAKE_MODULE_PATH} ) if(MITK_USE_DCMTK) if(${_dcmtk_dir_orig} MATCHES "${MITK_EXTERNAL_PROJECT_PREFIX}.*") # Help our FindDCMTK.cmake script find our super-build DCMTK set(DCMTK_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) else() # Use the original value set(DCMTK_DIR ${_dcmtk_dir_orig}) endif() endif() if(MITK_USE_DCMQI) # Due to the preferred CONFIG mode in find_package calls above, # the DCMQIConfig.cmake file is read, which does not provide useful # package information. We explictly need MODULE mode to find DCMQI. # Help our FindDCMQI.cmake script find our super-build DCMQI set(DCMQI_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) find_package(DCMQI REQUIRED) endif() if(MITK_USE_OpenIGTLink) link_directories(${OpenIGTLink_LIBRARY_DIRS}) endif() if(MITK_USE_OpenCL) find_package(OpenCL REQUIRED) endif() if(MITK_USE_OpenMP) find_package(OpenMP REQUIRED COMPONENTS CXX) else() find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) elseif(APPLE AND OpenMP_libomp_LIBRARY AND NOT OpenMP_CXX_LIB_NAMES) set(OpenMP_CXX_LIB_NAMES libomp CACHE STRING "" FORCE) get_filename_component(openmp_lib_dir "${OpenMP_libomp_LIBRARY}" DIRECTORY) set(openmp_include_dir "${openmp_lib_dir}/../include") if(EXISTS "${openmp_include_dir}") get_filename_component(openmp_include_dir "${openmp_include_dir}" REALPATH) set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${openmp_include_dir}" CACHE STRING "" FORCE) find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) endif() endif() endif() endif() # Qt support if(MITK_USE_Qt5) find_package(Qt5Core ${MITK_QT5_MINIMUM_VERSION} REQUIRED) # at least Core required get_target_property(_qmake_exec Qt5::qmake LOCATION) execute_process(COMMAND ${_qmake_exec} -query QT_INSTALL_BINS RESULT_VARIABLE _result OUTPUT_VARIABLE QT_BINARY_DIR ERROR_VARIABLE _error ) string(STRIP "${QT_BINARY_DIR}" QT_BINARY_DIR) if(_result OR NOT EXISTS "${QT_BINARY_DIR}") message(FATAL_ERROR "Could not determine Qt binary directory: ${_result} ${QT_BINARY_DIR} ${_error}") endif() find_program(QT_HELPGENERATOR_EXECUTABLE NAMES qhelpgenerator qhelpgenerator-qt5 qhelpgenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_COLLECTIONGENERATOR_EXECUTABLE NAMES qcollectiongenerator qcollectiongenerator-qt5 qcollectiongenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_ASSISTANT_EXECUTABLE NAMES assistant assistant-qt5 assistant5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_XMLPATTERNS_EXECUTABLE NAMES xmlpatterns PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) mark_as_advanced(QT_HELPGENERATOR_EXECUTABLE QT_COLLECTIONGENERATOR_EXECUTABLE QT_ASSISTANT_EXECUTABLE QT_XMLPATTERNS_EXECUTABLE ) if(MITK_USE_BLUEBERRY) option(BLUEBERRY_USE_QT_HELP "Enable support for integrating plugin documentation into Qt Help" ${DOXYGEN_FOUND}) mark_as_advanced(BLUEBERRY_USE_QT_HELP) # Sanity checks for in-application BlueBerry plug-in help generation if(BLUEBERRY_USE_QT_HELP) set(_force_blueberry_use_qt_help_to_off 0) if(NOT DOXYGEN_FOUND) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen was not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_LESS 1.8.7) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_HELPGENERATOR_EXECUTABLE) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because QT_HELPGENERATOR_EXECUTABLE is empty.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT MITK_USE_Qt5) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt5 is OFF.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_XMLPATTERNS_EXECUTABLE) message("You have enabled Qt Help support, but QT_XMLPATTERNS_EXECUTABLE is empty") set(_force_blueberry_use_qt_help_to_off 1) endif() if(_force_blueberry_use_qt_help_to_off) set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating plugin documentation into Qt Help" FORCE) endif() endif() if(BLUEBERRY_QT_HELP_REQUIRED AND NOT BLUEBERRY_USE_QT_HELP) message(FATAL_ERROR "BLUEBERRY_USE_QT_HELP is required to be set to ON") endif() endif() endif() #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) #[[ See T27701 # Initial cache for ProjectTemplate and PluginGenerator tests configure_file( CMake/mitkTestInitialCache.txt.in ${MITK_BINARY_DIR}/mitkTestInitialCache.txt @ONLY )]] # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch (const std::exception& e) { fprintf(stderr, \"%s\\n\", e.what()); return EXIT_FAILURE; } catch (...) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; }") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Set C/CXX and linker flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MITK_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MITK_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MITK_C_FLAGS_RELEASE}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MITK_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MITK_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MITK_MODULE_LINKER_FLAGS}") #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- add_subdirectory(Utilities) add_subdirectory(Modules) include("${CMAKE_CURRENT_SOURCE_DIR}/Modules/ModuleList.cmake") mitkFunctionWhitelistModules(MITK MITK_MODULES) set(MITK_ROOT_FOLDER_BACKUP "${MITK_ROOT_FOLDER}") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) get_filename_component(MITK_ROOT_FOLDER "${MITK_EXTENSION_DIR}" NAME) set(MITK_MODULES_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Modules") if(EXISTS "${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") set(MITK_MODULES "") include("${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") foreach(mitk_module ${MITK_MODULES}) add_subdirectory("${MITK_MODULES_EXTENSION_DIR}/${mitk_module}" "Modules/${mitk_module}") endforeach() endif() set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) endforeach() set(MITK_ROOT_FOLDER "${MITK_ROOT_FOLDER_BACKUP}") add_subdirectory(Wrapping) set(MITK_DOXYGEN_OUTPUT_DIR "${PROJECT_BINARY_DIR}/Documentation/Doxygen" CACHE PATH "Output directory for doxygen generated documentation.") if(MITK_USE_BLUEBERRY) include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") mitkFunctionWhitelistPlugins(MITK MITK_PLUGINS) set(mitk_plugins_fullpath "") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() set(MITK_PLUGIN_REGEX_LIST "") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PLUGINS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Plugins") if(EXISTS "${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") set(MITK_PLUGINS "") include("${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath "${MITK_PLUGINS_EXTENSION_DIR}/${mitk_plugin}") endforeach() endif() endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() if(MITK_BUILD_EXAMPLES) include("${CMAKE_CURRENT_SOURCE_DIR}/Examples/Plugins/PluginList.cmake") set(mitk_example_plugins_fullpath ) foreach(mitk_example_plugin ${MITK_EXAMPLE_PLUGINS}) list(APPEND mitk_example_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) list(APPEND mitk_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb MITK_PLUGIN_REGEX_LIST OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options set(mitk_apps_fullpath "") foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) if(${option_name}) list(APPEND mitk_apps_fullpath "${MITK_APPLICATIONS_EXTENSION_DIR}/${directory_name}^^${option_name}") endif() endforeach() endif() endforeach() if (mitk_plugins_fullpath) ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) endif() set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- if(DOXYGEN_FOUND) add_subdirectory(Documentation) endif() #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) set(ALL_MITK_APPS "") set(activated_apps_no 0) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) list(APPEND ALL_MITK_APPS "${MITK_EXTENSION_DIR}/Applications/${directory_name}^^${option_name}^^${executable_name}") if(${option_name} OR MITK_BUILD_ALL_APPS) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() endif() endforeach() list(LENGTH ALL_MITK_APPS app_count) if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${ALL_MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${target_dir}/CPackOptions.cmake") include("${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${target_dir}/CPackConfig.cmake") configure_file(${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${executable_name}") endif() endforeach() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- # ---------------- Export targets ----------------- set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) set(targets_to_export) get_property(module_targets GLOBAL PROPERTY MITK_MODULE_TARGETS) if(module_targets) list(APPEND targets_to_export ${module_targets}) endif() if(MITK_USE_BLUEBERRY) if(MITK_PLUGIN_LIBRARIES) list(APPEND targets_to_export ${MITK_PLUGIN_LIBRARIES}) endif() endif() export(TARGETS ${targets_to_export} APPEND FILE ${MITK_EXPORTS_FILE}) set(MITK_EXPORTED_TARGET_PROPERTIES ) foreach(target_to_export ${targets_to_export}) get_target_property(autoload_targets ${target_to_export} MITK_AUTOLOAD_TARGETS) if(autoload_targets) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_TARGETS \"${autoload_targets}\")") endif() get_target_property(autoload_dir ${target_to_export} MITK_AUTOLOAD_DIRECTORY) if(autoload_dir) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_DIRECTORY \"${autoload_dir}\")") endif() get_target_property(deprecated_module ${target_to_export} MITK_MODULE_DEPRECATED_SINCE) if(deprecated_module) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_MODULE_DEPRECATED_SINCE \"${deprecated_module}\")") endif() endforeach() # ---------------- External projects ----------------- get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) set(MITK_CONFIG_EXTERNAL_PROJECTS ) #string(REPLACE "^^" ";" _mitk_external_projects ${MITK_EXTERNAL_PROJECTS}) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} set(MITK_USE_${ep} ${MITK_USE_${ep}}) set(MITK_${ep}_DIR \"${${ep}_DIR}\") set(MITK_${ep}_COMPONENTS ${_components}) ") endforeach() foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(_components) set(_components_arg COMPONENTS \${_components}) else() set(_components_arg) endif() if(_package) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} if(MITK_USE_${ep}) set(${ep}_DIR \${MITK_${ep}_DIR}) if(MITK_${ep}_COMPONENTS) mitkMacroFindDependency(${_package} COMPONENTS \${MITK_${ep}_COMPONENTS}) else() mitkMacroFindDependency(${_package}) endif() endif()") endif() endforeach() # ---------------- Tools ----------------- configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) # ---------------- Configure files ----------------- configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake VERSION ${MITK_VERSION_STRING} COMPATIBILITY AnyNewerVersion) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) if(MSVC AND TARGET MitkWorkbench) set_directory_properties(PROPERTIES VS_STARTUP_PROJECT MitkWorkbench) endif() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/CMakeLists.txt") add_subdirectory("${MITK_APPLICATIONS_EXTENSION_DIR}" "Applications") endif() endforeach() #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) endif() #----------------------------------------------------------------------------- # Print configuration summary #----------------------------------------------------------------------------- message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL ) diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox index d35e6a6d97..216c7b0904 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/BasicDataTypes.dox @@ -1,60 +1,60 @@ /** \page BasicDataTypesPage Numeric MITK data types and their usage. -This page describes how to use very foundational data-tyes in MITK like mitk::Vector, mitk::Point and mitk::Matrix and +This page describes how to use very foundational data-tyes in MITK like mitk::Vector, mitk::Point and mitk::Matrix and how they can interact. \tableofcontents \section Structure Structure -The previously known, monolythic structure of putting every basic type into mitkVector.h has been broken and +The previously known, monolythic structure of putting every basic type into mitkVector.h has been broken and mitkVector.h has been split up into several, more atomic files, namely: -# mitkNumericConstants.h : contains basic constants like mitk::ScalarType or mitk::eps -# mitkArray.h : copy itk::FixedArrays (like itk::Point and itk::Vector) from and to types which implement the [] operator (array-types), like e.g. Plain Old Datatypes (POD) or the opencv vector -# mitkPoint.h : the mitk::Point class. This is basically the itk::Point with the added ToArray and Fill members to conveniently copy from and to array-types. In MITK, a point is considered a fixed geometric location and thus cannot be summed or multiplied. --# mitkVector.h : the mitk::Vector class. This is an itk::Vector, but with the possiblity to implicitly convert to vnl_vector and vnl_vector_fixed. In MITK, vectors denote directions and can be summed or multiplied with scalars. +-# mitkVector.h : the mitk::Vector class. This is an itk::Vector, but with the possiblity to implicitly convert to vnl_vector and vnl_vector_fixed. In MITK, vectors denote directions and can be summed or multiplied with scalars. -# mitkMatrix.h : the mitk::Matrix class. This is an itk::Matrix with the added ToArray and Fill members to conveniently copy from and to array-types. --# mitkQuaternion.h : a typedef to vnl_quaternion. --# mitkAffineTransform3D.h : a typedef to itk::AffineGeometryFrame +-# mitkQuaternion.h : a typedef to vnl_quaternion. +-# mitkAffineTransform3D.h : a typedef to itk::ScalableAffineTransform -# mitkNumericTypes.h : this file includes all of the above as a convenience header -The Equal methods to compare Points, Vectors, Matrices, ... have been moved into the respective files. +The Equal methods to compare Points, Vectors, Matrices, ... have been moved into the respective files. E.g., if you want to compare two vectors simply use the Equal method provided by mitkVector.h. \section Conversion Conversion between the data-types If you want to convert a mitk::Vector from or to a vnl_vector or a vnl_vector_fixed, simply write \code mitkVector3D = vnlVector3D; vnlVector3D_2 = mitkVector3D; \endcode Unfortunately this mechanism couldn't be implemented to every type of conversion. But in any case, the ToArray and FillVector/FillPoint/FillMatrix member functions can be used to convert to array-types. E.g., \code cv::Vec3d cvVec3D; - + mitkVector3D.ToArray(cvVec3D); mitkVector3D_2.FillVector(cvVec3D); \endcode No implicit conversion from mitk::Point to mitk::Vector was implemented as this would break with itk's concept of separating points and vectors. If you want to convert, use: \code mitkVector3D = mitkPoint3D.GetVectorFromOrigin(); - mitkPoint3D_2 = mitkVector3D; + mitkPoint3D_2 = mitkVector3D; \endcode more examples of how to convert between data types can be found in the tests: -# mitkArrayTypeConversionTest.cpp -# mitkPointTypeConversionTest.cpp -# mitkVectorTypeConversionTest.cpp -# mitkMatrixTypeConversionTest.cpp -*/ \ No newline at end of file +*/ diff --git a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/mitkColourImageProcessor.cpp b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/mitkColourImageProcessor.cpp index cebd6068d5..917a2725df 100644 --- a/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/mitkColourImageProcessor.cpp +++ b/Examples/Plugins/org.mitk.example.gui.imaging/src/internal/colourimageprocessing/mitkColourImageProcessor.cpp @@ -1,991 +1,991 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkColourImageProcessor.h" #include #include #include #include namespace mitk { mitkColourImageProcessor::mitkColourImageProcessor() {} mitkColourImageProcessor::~mitkColourImageProcessor() {} template class ScalarToRGBAConverter { const T *dataPtr; unsigned char *tmpPtr; int sizeX; int sizeY; int sizeZ; int sizeXY; int sizeXm1; int sizeYm1; int sizeZm1; mitk::TransferFunction::Pointer tf; public: ScalarToRGBAConverter(const T *_dataPtr, unsigned char *_tmpPtr, int _sizeX, int _sizeY, int _sizeZ, mitk::TransferFunction::Pointer _tf) { dataPtr = _dataPtr; tmpPtr = _tmpPtr; sizeX = _sizeX; sizeY = _sizeY; sizeZ = _sizeZ; sizeXY = sizeX * sizeY; sizeXm1 = sizeX - 1; sizeYm1 = sizeY - 1; sizeZm1 = sizeZ - 1; tf = _tf; } inline float sample(int x, int y, int z) { return float(dataPtr[x + y * sizeX + z * sizeXY]); } inline int clamp(int x) { if (x < 0) x = 0; else if (x > 255) x = 255; return x; } inline void write(int x, int y, int z, float grayValue, float gx, float gy, float gz) { /* gx /= aspect[0]; gy /= aspect[1]; gz /= aspect[2]; */ // Compute the gradient magnitude float t = sqrtf(gx * gx + gy * gy + gz * gz); int doff = x + y * sizeX + z * sizeXY; vtkPiecewiseFunction *opacityTransferFunction = tf->GetScalarOpacityFunction(); vtkPiecewiseFunction *gradientTransferFunction = tf->GetGradientOpacityFunction(); vtkColorTransferFunction *colorTransferFunction = tf->GetColorTransferFunction(); double rgb[3]; colorTransferFunction->GetColor(double(grayValue), rgb); double opacity = opacityTransferFunction->GetValue(double(grayValue)); opacity *= gradientTransferFunction->GetValue(double(0.5f * t)); tmpPtr[doff * 4 + 0] = int(rgb[0] * 255 + 0.5); tmpPtr[doff * 4 + 1] = int(rgb[1] * 255 + 0.5); tmpPtr[doff * 4 + 2] = int(rgb[2] * 255 + 0.5); tmpPtr[doff * 4 + 3] = int(opacity * 255 + 0.5); } inline void compute(int x, int y, int z) { float grayValue = sample(x, y, z); float gx, gy, gz; gx = sample(x + 1, y, z) - sample(x - 1, y, z); gy = sample(x, y + 1, z) - sample(x, y - 1, z); gz = sample(x, y, z + 1) - sample(x, y, z - 1); write(x, y, z, grayValue, gx, gy, gz); } inline void computeClamp(int x, int y, int z) { float grayValue = sample(x, y, z); float gx, gy, gz; if (x == 0) gx = 2.0f * (sample(x + 1, y, z) - grayValue); else if (x == sizeXm1) gx = 2.0f * (grayValue - sample(x - 1, y, z)); else gx = sample(x + 1, y, z) - sample(x - 1, y, z); if (y == 0) gy = 2.0f * (sample(x, y + 1, z) - grayValue); else if (y == sizeYm1) gy = 2.0f * (grayValue - sample(x, y - 1, z)); else gy = sample(x, y + 1, z) - sample(x, y - 1, z); if (z == 0) gz = 2.0f * (sample(x, y, z + 1) - grayValue); else if (z == sizeZm1) gz = 2.0f * (grayValue - sample(x, y, z - 1)); else gz = sample(x, y, z + 1) - sample(x, y, z - 1); write(x, y, z, grayValue, gx, gy, gz); } inline void compute1D(int y, int z) { int x; x = 0; computeClamp(x, y, z); x++; while (x < sizeX - 1) { compute(x, y, z); x++; } if (x < sizeX) { computeClamp(x, y, z); x++; } } inline void computeClamp1D(int y, int z) { int x; x = 0; while (x < sizeX) { computeClamp(x, y, z); x++; } } inline void computeClamp2D(int z) { int y; y = 0; while (y < sizeY) { computeClamp1D(y, z); y++; } } inline void compute2D(int z) { int y; y = 0; computeClamp1D(y, z); y++; while (y < sizeY - 1) { compute1D(y, z); y++; } if (y < sizeY) { computeClamp1D(y, z); y++; } } inline void fillSlices() { int z; for (z = 0; z < sizeZ; z++) { if (z == 0 || z == sizeZ - 1) computeClamp2D(z); else compute2D(z); } } }; template mitk::Image::Pointer mitkColourImageProcessor::ScalarToRGBA(itk::Image *input, mitk::TransferFunction::Pointer tf) { const TType *inputData = input->GetBufferPointer(); typename itk::Image::SizeType ioSize = input->GetLargestPossibleRegion().GetSize(); MITK_INFO << "size input image: " << ioSize[0] << ", " << ioSize[1] << ", " << ioSize[2]; MITK_INFO << "size voxel: " << ioSize[0] * ioSize[1] * ioSize[2]; int voxel = ioSize[0] * ioSize[1] * ioSize[2]; auto RGBABuffer = new unsigned char[4 * voxel]; // Convert volume { ScalarToRGBAConverter strc(inputData, RGBABuffer, ioSize[0], ioSize[1], ioSize[2], tf); strc.fillSlices(); } // Create MITK Image out of the raw data { mitk::Image::Pointer image = mitk::Image::New(); unsigned int dimensions[3]; dimensions[0] = ioSize[0]; dimensions[1] = ioSize[1]; dimensions[2] = ioSize[2]; mitk::PixelType pixelType(MakePixelType()); image->Initialize(pixelType, 3, dimensions); image->SetImportChannel(RGBABuffer, 0, Image::ManageMemory); return image; } } mitk::Image::Pointer mitkColourImageProcessor::convertToRGBAImage(mitk::Image::Pointer mitkInput, mitk::TransferFunction::Pointer tf) { MITK_INFO << "convertToRGBAImage"; mitk::Image::Pointer mitkResult = mitk::Image::New(); - if (mitkInput->GetPixelType().GetComponentType() == itk::ImageIOBase::CHAR) + if (mitkInput->GetPixelType().GetComponentType() == itk::IOComponentEnum::CHAR) { // cast to itkImage itk::Image::Pointer itkInput; mitk::CastToItkImage(mitkInput, itkInput); mitkResult = ScalarToRGBA(itkInput, tf); } - else if (mitkInput->GetPixelType().GetComponentType() == itk::ImageIOBase::SHORT) + else if (mitkInput->GetPixelType().GetComponentType() == itk::IOComponentEnum::SHORT) { // cast to itkImage itk::Image::Pointer itkInput; mitk::CastToItkImage(mitkInput, itkInput); mitkResult = ScalarToRGBA(itkInput, tf); } else { MITK_ERROR << "unsupported pixel type"; return nullptr; } mitkResult->SetClonedGeometry(mitkInput->GetGeometry()); return mitkResult; } template class ScalarBinaryToRGBAConverter { const T *dataPtr; const B *data2Ptr; unsigned char *tmpPtr; int sizeX; int sizeY; int sizeZ; int sizeXY; int sizeXm1; int sizeYm1; int sizeZm1; mitk::TransferFunction::Pointer tf; public: ScalarBinaryToRGBAConverter(const T *_dataPtr, const B *_data2Ptr, unsigned char *_tmpPtr, int _sizeX, int _sizeY, int _sizeZ, mitk::TransferFunction::Pointer _tf) { dataPtr = _dataPtr; data2Ptr = _data2Ptr; tmpPtr = _tmpPtr; sizeX = _sizeX; sizeY = _sizeY; sizeZ = _sizeZ; sizeXY = sizeX * sizeY; sizeXm1 = sizeX - 1; sizeYm1 = sizeY - 1; sizeZm1 = sizeZ - 1; tf = _tf; } inline float sample(int x, int y, int z) { return float(dataPtr[x + y * sizeX + z * sizeXY]); } inline bool sampleBinary(int x, int y, int z) { return data2Ptr[x + y * sizeX + z * sizeXY]; } inline int clamp(int x) { if (x < 0) x = 0; else if (x > 255) x = 255; return x; } inline void write(int x, int y, int z, float grayValue, float gx, float gy, float gz) { if (sampleBinary(x, y, z)) { /* gx /= aspect[0]; gy /= aspect[1]; gz /= aspect[2]; */ // Compute the gradient magnitude float t = sqrtf(gx * gx + gy * gy + gz * gz); int doff = x + y * sizeX + z * sizeXY; vtkPiecewiseFunction *opacityTransferFunction = tf->GetScalarOpacityFunction(); vtkPiecewiseFunction *gradientTransferFunction = tf->GetGradientOpacityFunction(); vtkColorTransferFunction *colorTransferFunction = tf->GetColorTransferFunction(); double rgb[3]; colorTransferFunction->GetColor(double(grayValue), rgb); double opacity = opacityTransferFunction->GetValue(double(grayValue)); opacity *= gradientTransferFunction->GetValue(double(0.5f * t)); tmpPtr[doff * 4 + 0] = int(rgb[0] * 255 + 0.5); tmpPtr[doff * 4 + 1] = int(rgb[1] * 255 + 0.5); tmpPtr[doff * 4 + 2] = int(rgb[2] * 255 + 0.5); tmpPtr[doff * 4 + 3] = int(opacity * 255 + 0.5); } else { int doff = x + y * sizeX + z * sizeXY; tmpPtr[doff * 4 + 0] = 0; tmpPtr[doff * 4 + 1] = 0; tmpPtr[doff * 4 + 2] = 0; tmpPtr[doff * 4 + 3] = 0; } } inline void compute(int x, int y, int z) { float grayValue = sample(x, y, z); float gx, gy, gz; gx = sample(x + 1, y, z) - sample(x - 1, y, z); gy = sample(x, y + 1, z) - sample(x, y - 1, z); gz = sample(x, y, z + 1) - sample(x, y, z - 1); write(x, y, z, grayValue, gx, gy, gz); } inline void computeClamp(int x, int y, int z) { float grayValue = sample(x, y, z); float gx, gy, gz; if (x == 0) gx = 2.0f * (sample(x + 1, y, z) - grayValue); else if (x == sizeXm1) gx = 2.0f * (grayValue - sample(x - 1, y, z)); else gx = sample(x + 1, y, z) - sample(x - 1, y, z); if (y == 0) gy = 2.0f * (sample(x, y + 1, z) - grayValue); else if (y == sizeYm1) gy = 2.0f * (grayValue - sample(x, y - 1, z)); else gy = sample(x, y + 1, z) - sample(x, y - 1, z); if (z == 0) gz = 2.0f * (sample(x, y, z + 1) - grayValue); else if (z == sizeZm1) gz = 2.0f * (grayValue - sample(x, y, z - 1)); else gz = sample(x, y, z + 1) - sample(x, y, z - 1); write(x, y, z, grayValue, gx, gy, gz); } inline void compute1D(int y, int z) { int x; x = 0; computeClamp(x, y, z); x++; while (x < sizeX - 1) { compute(x, y, z); x++; } if (x < sizeX) { computeClamp(x, y, z); x++; } } inline void computeClamp1D(int y, int z) { int x; x = 0; while (x < sizeX) { computeClamp(x, y, z); x++; } } inline void computeClamp2D(int z) { int y; y = 0; while (y < sizeY) { computeClamp1D(y, z); y++; } } inline void compute2D(int z) { int y; y = 0; computeClamp1D(y, z); y++; while (y < sizeY - 1) { compute1D(y, z); y++; } if (y < sizeY) { computeClamp1D(y, z); y++; } } inline void fillSlices() { int z; for (z = 0; z < sizeZ; z++) { if (z == 0 || z == sizeZ - 1) computeClamp2D(z); else compute2D(z); } } }; template mitk::Image::Pointer mitkColourImageProcessor::ScalarAndBinaryToRGBA(itk::Image *input, itk::Image *input2, mitk::TransferFunction::Pointer tf) { const TType *inputData = input->GetBufferPointer(); const BType *input2Data = input2->GetBufferPointer(); typename itk::Image::SizeType ioSize = input->GetLargestPossibleRegion().GetSize(); MITK_INFO << "size input image: " << ioSize[0] << ", " << ioSize[1] << ", " << ioSize[2]; MITK_INFO << "size voxel: " << ioSize[0] * ioSize[1] * ioSize[2]; int voxel = ioSize[0] * ioSize[1] * ioSize[2]; auto RGBABuffer = new unsigned char[4 * voxel]; // for(int i=0;i strc( inputData, input2Data, RGBABuffer, ioSize[0], ioSize[1], ioSize[2], tf); strc.fillSlices(); } // Create MITK Image out of the raw data { mitk::Image::Pointer image = mitk::Image::New(); unsigned int dimensions[3]; dimensions[0] = ioSize[0]; dimensions[1] = ioSize[1]; dimensions[2] = ioSize[2]; mitk::PixelType pixelType(MakePixelType()); image->Initialize(pixelType, 3, dimensions); image->SetImportChannel(RGBABuffer, 0, Image::ManageMemory); return image; } } mitk::Image::Pointer mitkColourImageProcessor::convertWithBinaryToRGBAImage(mitk::Image::Pointer input1, mitk::Image::Pointer input2, mitk::TransferFunction::Pointer tf) { MITK_INFO << "convertWithBinaryToRGBAImage"; itk::Image::Pointer inputCT; itk::Image::Pointer inputBinary; - if (input1->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR && - input2->GetPixelType().GetComponentType() == itk::ImageIOBase::SHORT) + if (input1->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR && + input2->GetPixelType().GetComponentType() == itk::IOComponentEnum::SHORT) { mitk::CastToItkImage(input1, inputBinary); mitk::CastToItkImage(input2, inputCT); } - else if (input1->GetPixelType().GetComponentType() == itk::ImageIOBase::SHORT && - input2->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR) + else if (input1->GetPixelType().GetComponentType() == itk::IOComponentEnum::SHORT && + input2->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR) { mitk::CastToItkImage(input1, inputCT); mitk::CastToItkImage(input2, inputBinary); } else { MITK_ERROR << "unsupported pixel type"; return nullptr; } mitk::Image::Pointer resultImage = ScalarAndBinaryToRGBA(inputCT, inputBinary, tf); resultImage->SetClonedGeometry(input1->GetGeometry()); return resultImage; } ////////////////////////////////////////// template class ScalarBinaryColorToRGBAConverter { const T *dataPtr; const B *data2Ptr; unsigned char *tmpPtr; int sizeX; int sizeY; int sizeZ; int sizeXY; int sizeXm1; int sizeYm1; int sizeZm1; mitk::TransferFunction::Pointer tf; int *color; public: ScalarBinaryColorToRGBAConverter(const T *_dataPtr, const B *_data2Ptr, unsigned char *_tmpPtr, int _sizeX, int _sizeY, int _sizeZ, mitk::TransferFunction::Pointer _tf, int *_color) { dataPtr = _dataPtr; data2Ptr = _data2Ptr; tmpPtr = _tmpPtr; sizeX = _sizeX; sizeY = _sizeY; sizeZ = _sizeZ; sizeXY = sizeX * sizeY; sizeXm1 = sizeX - 1; sizeYm1 = sizeY - 1; sizeZm1 = sizeZ - 1; tf = _tf; color = _color; } inline float sample(int x, int y, int z) { return float(dataPtr[x + y * sizeX + z * sizeXY]); } inline bool sampleBinary(int x, int y, int z) { return data2Ptr[x + y * sizeX + z * sizeXY]; } inline int clamp(int x) { if (x < 0) x = 0; else if (x > 255) x = 255; return x; } inline void write(int x, int y, int z, float grayValue, float gx, float gy, float gz) { if (sampleBinary(x, y, z)) { /* gx /= aspect[0]; gy /= aspect[1]; gz /= aspect[2]; */ // Compute the gradient magnitude float t = sqrtf(gx * gx + gy * gy + gz * gz); int doff = x + y * sizeX + z * sizeXY; vtkPiecewiseFunction *opacityTransferFunction = tf->GetScalarOpacityFunction(); vtkPiecewiseFunction *gradientTransferFunction = tf->GetGradientOpacityFunction(); vtkColorTransferFunction *colorTransferFunction = tf->GetColorTransferFunction(); double rgb[3]; colorTransferFunction->GetColor(double(grayValue), rgb); double opacity = opacityTransferFunction->GetValue(double(grayValue)); opacity *= gradientTransferFunction->GetValue(double(0.5f * t)); tmpPtr[doff * 4 + 0] = int(rgb[0] * 255 + 0.5); tmpPtr[doff * 4 + 1] = int(rgb[1] * 255 + 0.5); tmpPtr[doff * 4 + 2] = int(rgb[2] * 255 + 0.5); tmpPtr[doff * 4 + 3] = int(opacity * 255 + 0.5); tmpPtr[doff * 4 + 0] = color[0]; tmpPtr[doff * 4 + 1] = color[1]; tmpPtr[doff * 4 + 2] = color[2]; } else { int doff = x + y * sizeX + z * sizeXY; tmpPtr[doff * 4 + 0] = 0; tmpPtr[doff * 4 + 1] = 0; tmpPtr[doff * 4 + 2] = 0; tmpPtr[doff * 4 + 3] = 0; } } inline void compute(int x, int y, int z) { float grayValue = sample(x, y, z); float gx, gy, gz; gx = sample(x + 1, y, z) - sample(x - 1, y, z); gy = sample(x, y + 1, z) - sample(x, y - 1, z); gz = sample(x, y, z + 1) - sample(x, y, z - 1); write(x, y, z, grayValue, gx, gy, gz); } inline void computeClamp(int x, int y, int z) { float grayValue = sample(x, y, z); float gx, gy, gz; if (x == 0) gx = 2.0f * (sample(x + 1, y, z) - grayValue); else if (x == sizeXm1) gx = 2.0f * (grayValue - sample(x - 1, y, z)); else gx = sample(x + 1, y, z) - sample(x - 1, y, z); if (y == 0) gy = 2.0f * (sample(x, y + 1, z) - grayValue); else if (y == sizeYm1) gy = 2.0f * (grayValue - sample(x, y - 1, z)); else gy = sample(x, y + 1, z) - sample(x, y - 1, z); if (z == 0) gz = 2.0f * (sample(x, y, z + 1) - grayValue); else if (z == sizeZm1) gz = 2.0f * (grayValue - sample(x, y, z - 1)); else gz = sample(x, y, z + 1) - sample(x, y, z - 1); write(x, y, z, grayValue, gx, gy, gz); } inline void compute1D(int y, int z) { int x; x = 0; computeClamp(x, y, z); x++; while (x < sizeX - 1) { compute(x, y, z); x++; } if (x < sizeX) { computeClamp(x, y, z); x++; } } inline void computeClamp1D(int y, int z) { int x; x = 0; while (x < sizeX) { computeClamp(x, y, z); x++; } } inline void computeClamp2D(int z) { int y; y = 0; while (y < sizeY) { computeClamp1D(y, z); y++; } } inline void compute2D(int z) { int y; y = 0; computeClamp1D(y, z); y++; while (y < sizeY - 1) { compute1D(y, z); y++; } if (y < sizeY) { computeClamp1D(y, z); y++; } } inline void fillSlices() { int z; for (z = 0; z < sizeZ; z++) { if (z == 0 || z == sizeZ - 1) computeClamp2D(z); else compute2D(z); } } }; template mitk::Image::Pointer mitkColourImageProcessor::ScalarAndBinaryAndColorToRGBA(itk::Image *input, itk::Image *input2, mitk::TransferFunction::Pointer tf, int *color) { const TType *inputData = input->GetBufferPointer(); const BType *input2Data = input2->GetBufferPointer(); typename itk::Image::SizeType ioSize = input->GetLargestPossibleRegion().GetSize(); MITK_INFO << "size input image: " << ioSize[0] << ", " << ioSize[1] << ", " << ioSize[2]; MITK_INFO << "size voxel: " << ioSize[0] * ioSize[1] * ioSize[2]; int voxel = ioSize[0] * ioSize[1] * ioSize[2]; auto RGBABuffer = new unsigned char[4 * voxel]; // for(int i=0;i strc( inputData, input2Data, RGBABuffer, ioSize[0], ioSize[1], ioSize[2], tf, color); strc.fillSlices(); } // Create MITK Image out of the raw data { mitk::Image::Pointer image = mitk::Image::New(); unsigned int dimensions[3]; dimensions[0] = ioSize[0]; dimensions[1] = ioSize[1]; dimensions[2] = ioSize[2]; mitk::PixelType pixelType(MakePixelType()); image->Initialize(pixelType, 3, dimensions); image->SetImportChannel(RGBABuffer, 0, Image::ManageMemory); // image->GetGeometry()->SetSpacing(input->GetSpacing()); // image->GetGeometry()->SetOrigin(input->GetOrigin()); return image; } } mitk::Image::Pointer mitkColourImageProcessor::convertWithBinaryAndColorToRGBAImage( mitk::Image::Pointer input1, mitk::Image::Pointer input2, mitk::TransferFunction::Pointer tf, int *color) { MITK_INFO << "convertWithBinaryToRGBAImage"; itk::Image::Pointer inputCT; itk::Image::Pointer inputBinary; - if (input1->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR && - input2->GetPixelType().GetComponentType() == itk::ImageIOBase::SHORT) + if (input1->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR && + input2->GetPixelType().GetComponentType() == itk::IOComponentEnum::SHORT) { mitk::CastToItkImage(input1, inputBinary); mitk::CastToItkImage(input2, inputCT); } - else if (input1->GetPixelType().GetComponentType() == itk::ImageIOBase::SHORT && - input2->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR) + else if (input1->GetPixelType().GetComponentType() == itk::IOComponentEnum::SHORT && + input2->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR) { mitk::CastToItkImage(input1, inputCT); mitk::CastToItkImage(input2, inputBinary); } else { MITK_ERROR << "unsupported pixel type"; return nullptr; } mitk::Image::Pointer resultImage = ScalarAndBinaryAndColorToRGBA(inputCT, inputBinary, tf, color); resultImage->SetClonedGeometry(input1->GetGeometry()); return resultImage; } mitk::Image::Pointer mitkColourImageProcessor::CombineRGBAImage( const unsigned char *input, const unsigned char *input2, int sizeX, int sizeY, int sizeZ) { int voxel = sizeX * sizeY * sizeZ; auto RGBABuffer = new unsigned char[4 * voxel]; // Convert volume { for (int r = 0; r < voxel; r++) { int rgbaInput1[4]; int rgbaInput2[4]; rgbaInput1[0] = input[r * 4 + 0]; rgbaInput1[1] = input[r * 4 + 1]; rgbaInput1[2] = input[r * 4 + 2]; rgbaInput1[3] = input[r * 4 + 3]; rgbaInput2[0] = input2[r * 4 + 0]; rgbaInput2[1] = input2[r * 4 + 1]; rgbaInput2[2] = input2[r * 4 + 2]; rgbaInput2[3] = input2[r * 4 + 3]; int result[4]; /* float fac1 = rgbaInput1[3]/255.0f; float fac2 = rgbaInput2[3]/255.0f; float sum = fac1+fac2; float cor = 1.0f; if(sum > 1.0f) cor = 1.0f/sum; fac1 *= cor; fac2 *= cor; result[0]= clamp(int(fac1 * rgbaInput1[0] + fac2 * rgbaInput2[0] + 0.5f)); result[1]= clamp(int(fac1 * rgbaInput1[1] + fac2 * rgbaInput2[1] + 0.5f)); result[2]= clamp(int(fac1 * rgbaInput1[2] + fac2 * rgbaInput2[2] + 0.5f)); result[3]= clamp(int(fac1 * rgbaInput1[3] + fac2 * rgbaInput2[3] + 0.5f)); */ if (rgbaInput1[3]) { result[0] = rgbaInput1[0]; result[1] = rgbaInput1[1]; result[2] = rgbaInput1[2]; result[3] = rgbaInput1[3]; } else { result[0] = rgbaInput2[0]; result[1] = rgbaInput2[1]; result[2] = rgbaInput2[2]; result[3] = rgbaInput2[3]; } RGBABuffer[r * 4 + 0] = result[0]; RGBABuffer[r * 4 + 1] = result[1]; RGBABuffer[r * 4 + 2] = result[2]; RGBABuffer[r * 4 + 3] = result[3]; } } // Create MITK Image out of the raw data { mitk::Image::Pointer image = mitk::Image::New(); unsigned int dimensions[3]; dimensions[0] = sizeX; dimensions[1] = sizeY; dimensions[2] = sizeZ; mitk::PixelType pixelType(MakePixelType()); image->Initialize(pixelType, 3, dimensions); image->SetImportChannel(RGBABuffer, 0, Image::ManageMemory); return image; } } mitk::Image::Pointer mitkColourImageProcessor::combineRGBAImage(mitk::Image::Pointer input1, mitk::Image::Pointer input2) { // Order access to a whole Image object try { mitk::ImageReadAccessor img1(input1); const unsigned char *data1 = (const unsigned char *)img1.GetData(); mitk::ImageReadAccessor img2(input2); const unsigned char *data2 = (const unsigned char *)img2.GetData(); unsigned int *dim = input1->GetDimensions(); return CombineRGBAImage(data1, data2, dim[0], dim[1], dim[2]); } catch ( const mitk::Exception &e ) { MITK_ERROR << "mitkColourImageProcessor::combineRGBAImage - No access to image data possible." << e.what(); return nullptr; } } } // end namespace mitk diff --git a/Examples/Plugins/org.mitk.example.gui.regiongrowing/CMakeLists.txt b/Examples/Plugins/org.mitk.example.gui.regiongrowing/CMakeLists.txt index ee279d1db8..30b07ca95e 100644 --- a/Examples/Plugins/org.mitk.example.gui.regiongrowing/CMakeLists.txt +++ b/Examples/Plugins/org.mitk.example.gui.regiongrowing/CMakeLists.txt @@ -1,8 +1,9 @@ project(org_mitk_example_gui_regiongrowing) mitk_create_plugin( EXPORT_DIRECTIVE REGIONGROWING_EXPORT EXPORTED_INCLUDE_SUFFIXES src MODULE_DEPENDS MitkQtWidgetsExt + PACKAGE_DEPENDS ITK|RegionGrowing NO_INSTALL ) diff --git a/Examples/Tutorial/Step6/CMakeLists.txt b/Examples/Tutorial/Step6/CMakeLists.txt index 8fb1bbb466..c486d97f84 100644 --- a/Examples/Tutorial/Step6/CMakeLists.txt +++ b/Examples/Tutorial/Step6/CMakeLists.txt @@ -1,3 +1,4 @@ mitk_create_executable( DEPENDS MitkQtWidgetsExt + PACKAGE_DEPENDS ITK|RegionGrowing ) diff --git a/Examples/Tutorial/Step7/CMakeLists.txt b/Examples/Tutorial/Step7/CMakeLists.txt index 13c7b070a6..f3a9489e05 100644 --- a/Examples/Tutorial/Step7/CMakeLists.txt +++ b/Examples/Tutorial/Step7/CMakeLists.txt @@ -1,5 +1,6 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../Step6) mitk_create_executable( DEPENDS MitkQtWidgetsExt + PACKAGE_DEPENDS ITK|RegionGrowing ) diff --git a/Examples/Tutorial/Step8/CMakeLists.txt b/Examples/Tutorial/Step8/CMakeLists.txt index a090d34d9e..aa07199000 100644 --- a/Examples/Tutorial/Step8/CMakeLists.txt +++ b/Examples/Tutorial/Step8/CMakeLists.txt @@ -1,8 +1,9 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../Step6 ${CMAKE_CURRENT_SOURCE_DIR}/../Step7 ) mitk_create_executable( DEPENDS MitkQtWidgetsExt + PACKAGE_DEPENDS ITK|RegionGrowing ) diff --git a/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h b/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h index 6390dfb533..72e6d9ca55 100644 --- a/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h +++ b/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.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 mitkCropTimestepsImageFilter_h #define mitkCropTimestepsImageFilter_h #include "MitkAlgorithmsExtExports.h" #include namespace mitk { /** \brief Crops timesteps at 2D+t and 3D+t images * \details The filter is able to crop timesteps in front and/or at the end. * Internally, a new image is created with the remaining volumes. The geometries and properties of * the input image are transferred to the output image. */ class MITKALGORITHMSEXT_EXPORT CropTimestepsImageFilter : public SubImageSelector { public: mitkClassMacro(CropTimestepsImageFilter, SubImageSelector); itkFactorylessNewMacro(Self); /*! * \brief Sets the input image * \pre the image has 4 Dimensions * \pre the image has >1 timestep * \pre the image is valid (valid geometry and volume) */ void SetInput(const InputImageType* image) override; void SetInput(unsigned int index, const InputImageType* image) override; /*! * \brief The last timestep to retain * \details Set to the maximum timestep of the input as default * \note if the last timestep is larger than the maximum timestep of the input, * it is corrected to the maximum timestep. * \exception if (LowerBoundaryTimestep > UpperBoundaryTimestep) */ itkSetMacro(UpperBoundaryTimestep, unsigned int); itkGetConstMacro(UpperBoundaryTimestep, unsigned int); /*! * \brief The first timestep to retain * \details Set to 0 as default * \exception if (LowerBoundaryTimestep > UpperBoundaryTimestep) */ itkSetMacro(LowerBoundaryTimestep, unsigned int); itkGetConstMacro(LowerBoundaryTimestep, unsigned int); private: using Superclass::SetInput; CropTimestepsImageFilter() = default; ~CropTimestepsImageFilter() override = default; void GenerateData() override; - void VerifyInputInformation() override; + void VerifyInputInformation() const override; void VerifyInputImage(const mitk::Image* inputImage) const; void GenerateOutputInformation() override; mitk::SlicedData::RegionType ComputeDesiredRegion() const; mitk::TimeGeometry::Pointer AdaptTimeGeometry(mitk::TimeGeometry::ConstPointer sourceGeometry, unsigned int startTimestep, unsigned int endTimestep) const; unsigned int m_UpperBoundaryTimestep = std::numeric_limits::max(); unsigned int m_LowerBoundaryTimestep = 0; mitk::SlicedData::RegionType m_DesiredRegion; }; } #endif diff --git a/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h b/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h index 9ea599e365..0af199cbb1 100644 --- a/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h +++ b/Modules/AlgorithmsExt/include/mitkNonBlockingAlgorithm.h @@ -1,251 +1,249 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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_NON_BLOCKING_ALGORITHM_H_INCLUDED_DFARdfWN1tr #define MITK_NON_BLOCKING_ALGORITHM_H_INCLUDED_DFARdfWN1tr #include "MitkAlgorithmsExtExports.h" -#include #include #include -#include #include #include "mitkCommon.h" #include "mitkDataStorage.h" #include "mitkProperties.h" #include "mitkPropertyList.h" #include "mitkSmartPointerProperty.h" #include "mitkWeakPointer.h" #include "mitkImage.h" #include "mitkSurface.h" +#include #include #include /// from itkNewMacro(), additionally calls Initialize(), because this couldn't be done from the constructor of /// NonBlockingAlgorithm /// (you can't call virtual functions from the constructor of the superclass) #define mitkAlgorithmNewMacro(classname) \ \ static Pointer \ New(void) \ { \ classname *rawPtr = new classname(); \ Pointer smartPtr = rawPtr; \ rawPtr->UnRegister(); \ rawPtr->Initialize(); \ return smartPtr; \ \ } \ \ virtual::itk::LightObject::Pointer \ CreateAnother(void) const override \ \ { \ Pointer smartPtr = classname::New(); \ ::itk::LightObject::Pointer lightPtr = smartPtr.GetPointer(); \ smartPtr->Initialize(this); \ return lightPtr; \ \ } namespace mitk { /*! Invokes ResultsAvailable with each new result done centralize use of itk::MultiThreader in this class @todo do the property-handling in this class @todo process "incoming" events in this class @todo sollen segmentierungs-dinger von mitk::ImageSource erben? Ivo fragen, wie das mit AllocateOutputs, etc. gehen soll eine ImageSourceAlgorithm koennte dann die noetigen Methoden wie GenerateData(), GetOutput() ueberschreiben, so dass von dort aus die Methoden von NonBlockingAlgorithm aufgerufen werden. Erben v.a. um die Output-Sachen zu uebernehmen, die Anpassungen das einfuehren einer Zwischenklasse, um die Interaces zu verheiraten. */ class MITKALGORITHMSEXT_EXPORT NonBlockingAlgorithm : public itk::Object { public: // for threading class MITKALGORITHMSEXT_EXPORT ThreadParameters { public: itk::SmartPointer m_Algorithm; }; mitkClassMacroItkParent(NonBlockingAlgorithm, itk::Object); void SetDataStorage(DataStorage &storage); DataStorage *GetDataStorage(); // parameter setting /// For any kind of normal types template void SetParameter(const char *parameter, const T &value) { // MITK_INFO << "SetParameter(" << parameter << ") " << typeid(T).name() << std::endl; // m_ParameterListMutex->Lock(); m_Parameters->SetProperty(parameter, GenericProperty::New(value)); // m_ParameterListMutex->Unlock(); } /// For any kind of smart pointers template void SetPointerParameter(const char *parameter, const itk::SmartPointer &value) { // MITK_INFO << this << "->SetParameter smartpointer(" << parameter << ") " << typeid(itk::SmartPointer).name() // << std::endl; - m_ParameterListMutex->Lock(); + m_ParameterListMutex.lock(); m_Parameters->SetProperty(parameter, SmartPointerProperty::New(value.GetPointer())); - m_ParameterListMutex->Unlock(); + m_ParameterListMutex.unlock(); } // virtual void SetParameter( const char*, mitk::BaseProperty* ); // for "number of iterations", ... // create some property observing to inform algorithm object about changes // perhaps some TriggerParameter(string) macro that creates an observer for changes in a specific property like // "2ndPoint" for LineAlgorithms /// For any kind of BaseData, like Image, Surface, etc. Will be stored inside some SmartPointerProperty void SetPointerParameter(const char *parameter, BaseData *value); /// For any kind of ITK images (C pointers) template void SetItkImageAsMITKImagePointerParameter(const char *parameter, itk::Image *itkImage) { // MITK_INFO << "SetParameter ITK image(" << parameter << ") " << typeid(itk::Image).name() << std::endl; // create an MITK image for that mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage = ImportItkImage(itkImage); SetPointerParameter(parameter, mitkImage); } /// For any kind of ITK images (smartpointers) template void SetItkImageAsMITKImagePointerParameter(const char *parameter, const itk::SmartPointer> &itkImage) { // MITK_INFO << "SetParameter ITK image(" << parameter << ") " << typeid(itk::SmartPointer >).name() << std::endl; // create an MITK image for that mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage = ImportItkImage(itkImage); SetPointerParameter(parameter, mitkImage); } // parameter getting template void GetParameter(const char *parameter, T &value) const { // MITK_INFO << "GetParameter normal(" << parameter << ") " << typeid(T).name() << std::endl; // m_ParameterListMutex->Lock(); BaseProperty *p = m_Parameters->GetProperty(parameter); GenericProperty *gp = dynamic_cast *>(p); if (gp) { value = gp->GetValue(); // m_ParameterListMutex->Unlock(); return; } // m_ParameterListMutex->Unlock(); std::string error("There is no parameter \""); error += parameter; error += '"'; throw std::invalid_argument(error); } template void GetPointerParameter(const char *parameter, itk::SmartPointer &value) const { // MITK_INFO << this << "->GetParameter smartpointer(" << parameter << ") " << typeid(itk::SmartPointer).name() // << std::endl; // m_ParameterListMutex->Lock(); BaseProperty *p = m_Parameters->GetProperty(parameter); if (p) { SmartPointerProperty *spp = dynamic_cast(p); if (spp) { T *t = dynamic_cast(spp->GetSmartPointer().GetPointer()); value = t; // m_ParameterListMutex->Unlock(); return; } } // m_ParameterListMutex->Unlock(); std::string error("There is no parameter \""); error += parameter; error += '"'; throw std::invalid_argument(error); } // start/stop functions virtual void Reset(); void StartAlgorithm(); // for those who want to trigger calculations on their own // --> need for an OPTION: manual/automatic starting void StartBlockingAlgorithm(); // for those who want to trigger calculations on their own void StopAlgorithm(); void TriggerParameterModified(const itk::EventObject &); void ThreadedUpdateSuccessful(const itk::EventObject &); void ThreadedUpdateFailed(const itk::EventObject &); protected: NonBlockingAlgorithm(); // use smart pointers ~NonBlockingAlgorithm() override; void DefineTriggerParameter(const char *); void UnDefineTriggerParameter(const char *); virtual void Initialize(const NonBlockingAlgorithm *other = nullptr); virtual bool ReadyToRun(); virtual bool ThreadedUpdateFunction(); // will be called from a thread after calling StartAlgorithm virtual void ThreadedUpdateSuccessful(); // will be called after the ThreadedUpdateFunction() returned virtual void ThreadedUpdateFailed(); // will when ThreadedUpdateFunction() returns false PropertyList::Pointer m_Parameters; WeakPointer m_DataStorage; private: - static ITK_THREAD_RETURN_TYPE StaticNonBlockingAlgorithmThread(void *param); + static itk::ITK_THREAD_RETURN_TYPE StaticNonBlockingAlgorithmThread(ThreadParameters *param); typedef std::map MapTypeStringUInt; MapTypeStringUInt m_TriggerPropertyConnections; - itk::FastMutexLock::Pointer m_ParameterListMutex; + std::mutex m_ParameterListMutex; - int m_ThreadID; int m_UpdateRequests; ThreadParameters m_ThreadParameters; - itk::MultiThreader::Pointer m_MultiThreader; + std::thread m_Thread; bool m_KillRequest; }; } // namespace #include "mitkNonBlockingAlgorithmEvents.h" #endif diff --git a/Modules/AlgorithmsExt/src/mitkAnisotropicRegistrationCommon.cpp b/Modules/AlgorithmsExt/src/mitkAnisotropicRegistrationCommon.cpp index e904b06752..00a8976653 100644 --- a/Modules/AlgorithmsExt/src/mitkAnisotropicRegistrationCommon.cpp +++ b/Modules/AlgorithmsExt/src/mitkAnisotropicRegistrationCommon.cpp @@ -1,100 +1,100 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitk::AnisotropicRegistrationCommon::WeightMatrix mitk::AnisotropicRegistrationCommon::CalculateWeightMatrix( const CovarianceMatrix &sigma_X, const CovarianceMatrix &sigma_Y) { WeightMatrix returnValue; WeightMatrix sum = sigma_X + sigma_Y; - vnl_svd svd(sum.GetVnlMatrix()); + vnl_svd svd(sum.GetVnlMatrix().as_ref()); WeightMatrix diag; diag.Fill(0.0); diag[0][0] = 1.0 / sqrt(svd.W(0)); diag[1][1] = 1.0 / sqrt(svd.W(1)); diag[2][2] = 1.0 / sqrt(svd.W(2)); WeightMatrix V; // convert vnl matrix to itk matrix... for (unsigned int i = 0; i < 3; ++i) for (unsigned int j = 0; j < 3; ++j) V[i][j] = svd.V()[i][j]; // add weighting matrix for point j1 (corresponding to identity transform) returnValue = V * diag * V.GetTranspose(); return returnValue; } void mitk::AnisotropicRegistrationCommon::TransformPoints(vtkPoints *src, vtkPoints *dst, const Rotation &rotation, const Translation &translation) { #pragma omp parallel for for (int i = 0; i < src->GetNumberOfPoints(); ++i) { double p_in[3]; double p_out[3]; src->GetPoint(i, p_in); for (unsigned int j = 0; j < 3; ++j) { p_out[j] = p_in[0] * rotation[j][0] + p_in[1] * rotation[j][1] + p_in[2] * rotation[j][2] + translation[j]; } dst->SetPoint(i, p_out); } } void mitk::AnisotropicRegistrationCommon::PropagateMatrices(const MatrixList &src, MatrixList &dst, const Rotation &rotation) { const vnl_matrix_fixed rotationT = rotation.GetTranspose(); #pragma omp parallel for for (int i = 0; i < static_cast(src.size()); ++i) { dst[i] = rotation * src[i] * rotationT; } } double mitk::AnisotropicRegistrationCommon::ComputeTargetRegistrationError(const mitk::PointSet *movingTargets, const mitk::PointSet *fixedTargets, const Rotation &rotation, const Translation &translation) { double tre = 0.0; for (int i = 0; i < movingTargets->GetSize(); ++i) { mitk::Point3D pm = movingTargets->GetPoint(i); mitk::Point3D ps = fixedTargets->GetPoint(i); // transform point pm = rotation * pm + translation; const double dist = (ps[0] - pm[0]) * (ps[0] - pm[0]) + (ps[1] - pm[1]) * (ps[1] - pm[1]) + (ps[2] - pm[2]) * (ps[2] - pm[2]); tre += dist; } tre /= movingTargets->GetSize(); return sqrt(tre); } diff --git a/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp index d5b7b29842..f892632904 100644 --- a/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp +++ b/Modules/AlgorithmsExt/src/mitkAutoCropImageFilter.cpp @@ -1,362 +1,362 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkAutoCropImageFilter.h" #include "mitkGeometry3D.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageReadAccessor.h" #include "mitkPlaneGeometry.h" #include "mitkStatusBar.h" #include #include #include mitk::AutoCropImageFilter::AutoCropImageFilter() : m_BackgroundValue(0), m_MarginFactor(1.0), m_TimeSelector(nullptr), m_OverrideCroppingRegion(false) { } mitk::AutoCropImageFilter::~AutoCropImageFilter() { } template void mitk::AutoCropImageFilter::ITKCrop3DImage(itk::Image *inputItkImage, unsigned int timestep) { if (inputItkImage == nullptr) { mitk::StatusBar::GetInstance()->DisplayErrorText( "An internal error occurred. Can't convert Image. Please report to bugs@mitk.org"); MITK_ERROR << "image is nullptr...returning" << std::endl; return; } typedef itk::Image InternalImageType; typedef typename InternalImageType::Pointer InternalImagePointer; typedef itk::RegionOfInterestImageFilter ROIFilterType; typedef typename itk::RegionOfInterestImageFilter::Pointer ROIFilterPointer; InternalImagePointer outputItk = InternalImageType::New(); ROIFilterPointer roiFilter = ROIFilterType::New(); roiFilter->SetInput(0, inputItkImage); roiFilter->SetRegionOfInterest(this->GetCroppingRegion()); roiFilter->Update(); outputItk = roiFilter->GetOutput(); outputItk->DisconnectPipeline(); mitk::Image::Pointer newMitkImage = mitk::Image::New(); mitk::CastToMitkImage(outputItk, newMitkImage); MITK_INFO << "Crop-Output dimension: " << (newMitkImage->GetDimension() == 3) << " Filter-Output dimension: " << this->GetOutput()->GetDimension() << " Timestep: " << timestep; mitk::ImageReadAccessor newMitkImgAcc(newMitkImage); this->GetOutput()->SetVolume(newMitkImgAcc.GetData(), timestep); } void mitk::AutoCropImageFilter::GenerateOutputInformation() { mitk::Image::Pointer input = const_cast(this->GetInput()); mitk::Image::Pointer output = this->GetOutput(); if (input->GetDimension() <= 2) { MITK_ERROR << "Only 3D any 4D images are supported." << std::endl; return; } ComputeNewImageBounds(); if ((output->IsInitialized()) && (output->GetPipelineMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; itkDebugMacro(<< "GenerateOutputInformation()"); // PART I: initialize input requested region. We do this already here (and not // later when GenerateInputRequestedRegion() is called), because we // also need the information to setup the output. // pre-initialize input-requested-region to largest-possible-region // and correct time-region; spatial part will be cropped by // bounding-box of bounding-object below m_InputRequestedRegion = input->GetLargestPossibleRegion(); // build region out of index and size calculated in ComputeNewImageBounds() mitk::SlicedData::IndexType index; index[0] = m_RegionIndex[0]; index[1] = m_RegionIndex[1]; index[2] = m_RegionIndex[2]; index[3] = m_InputRequestedRegion.GetIndex()[3]; index[4] = m_InputRequestedRegion.GetIndex()[4]; mitk::SlicedData::SizeType size; size[0] = m_RegionSize[0]; size[1] = m_RegionSize[1]; size[2] = m_RegionSize[2]; size[3] = m_InputRequestedRegion.GetSize()[3]; size[4] = m_InputRequestedRegion.GetSize()[4]; mitk::SlicedData::RegionType cropRegion(index, size); // crop input-requested-region with cropping region computed from the image data if (m_InputRequestedRegion.Crop(cropRegion) == false) { // crop not possible => do nothing: set time size to 0. size.Fill(0); m_InputRequestedRegion.SetSize(size); return; } // set input-requested-region, because we access it later in // GenerateInputRequestedRegion (there we just set the time) input->SetRequestedRegion(&m_InputRequestedRegion); // PART II: initialize output image unsigned int dimension = input->GetDimension(); auto dimensions = new unsigned int[dimension]; itk2vtk(m_InputRequestedRegion.GetSize(), dimensions); if (dimension > 3) memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3) * sizeof(unsigned int)); // create basic slicedGeometry that will be initialized below output->Initialize(mitk::PixelType(GetOutputPixelType()), dimension, dimensions); delete[] dimensions; // clone the IndexToWorldTransform from the input, otherwise we will overwrite it, when adjusting the origin of the // output image!! itk::ScalableAffineTransform::Pointer cloneTransform = itk::ScalableAffineTransform::New(); cloneTransform->Compose(input->GetGeometry()->GetIndexToWorldTransform()); output->GetGeometry()->SetIndexToWorldTransform(cloneTransform.GetPointer()); // Position the output Image to match the corresponding region of the input image mitk::SlicedGeometry3D *slicedGeometry = output->GetSlicedGeometry(); mitk::SlicedGeometry3D::Pointer inputGeometry = input->GetSlicedGeometry(); const mitk::SlicedData::IndexType &start = m_InputRequestedRegion.GetIndex(); mitk::Point3D origin; vtk2itk(start, origin); input->GetSlicedGeometry()->IndexToWorld(origin, origin); slicedGeometry->SetOrigin(origin); // get the PlaneGeometry for the first slice of the original image mitk::PlaneGeometry::Pointer plane = dynamic_cast(inputGeometry->GetPlaneGeometry(0)->Clone().GetPointer()); assert(plane); // re-initialize the plane according to the new requirements: // dimensions of the cropped image // right- and down-vector as well as spacing do not change, so use the ones from // input image ScalarType dimX = output->GetDimensions()[0]; ScalarType dimY = output->GetDimensions()[1]; mitk::Vector3D right = plane->GetAxisVector(0); mitk::Vector3D down = plane->GetAxisVector(1); mitk::Vector3D spacing = plane->GetSpacing(); plane->InitializeStandardPlane(dimX, dimY, right, down, &spacing); // set the new origin on the PlaneGeometry as well plane->SetOrigin(origin); // re-initialize the slicedGeometry with the correct planeGeometry // in order to get a fully initialized SlicedGeometry3D slicedGeometry->InitializeEvenlySpaced( plane, inputGeometry->GetSpacing()[2], output->GetSlicedGeometry()->GetSlices()); mitk::TimeGeometry *timeSlicedGeometry = output->GetTimeGeometry(); auto *propTimeGeometry = dynamic_cast(timeSlicedGeometry); propTimeGeometry->Initialize(slicedGeometry, output->GetDimension(3)); m_TimeOfHeaderInitialization.Modified(); output->SetPropertyList(input->GetPropertyList()->Clone()); } void mitk::AutoCropImageFilter::GenerateData() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if (input.IsNull()) return; if (input->GetDimension() <= 2) { MITK_ERROR << "Only 3D and 4D images supported"; return; } if ((output->IsInitialized() == false)) return; if (m_TimeSelector.IsNull()) m_TimeSelector = mitk::ImageTimeSelector::New(); m_TimeSelector->SetInput(input); mitk::SlicedData::RegionType outputRegion = input->GetRequestedRegion(); int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); for (int timestep = tstart; timestep < tmax; ++timestep) { m_TimeSelector->SetTimeNr(timestep); m_TimeSelector->UpdateLargestPossibleRegion(); AccessFixedDimensionByItk_1(m_TimeSelector->GetOutput(), ITKCrop3DImage, 3, timestep); } // this->GetOutput()->Update(); // Not sure if this is necessary... m_TimeOfHeaderInitialization.Modified(); } void mitk::AutoCropImageFilter::ComputeNewImageBounds() { mitk::Image::ConstPointer inputMitk = this->GetInput(); if (m_OverrideCroppingRegion) { for (unsigned int i = 0; i < 3; ++i) { m_RegionIndex[i] = m_CroppingRegion.GetIndex()[i]; m_RegionSize[i] = m_CroppingRegion.GetSize()[i]; if (m_RegionIndex[i] >= static_cast(inputMitk->GetDimension(i))) { itkExceptionMacro("Cropping index is not inside the image. " << std::endl << "Index:" << std::endl << m_CroppingRegion.GetIndex() << std::endl << "Size:" << std::endl << m_CroppingRegion.GetSize()); } if (m_RegionIndex[i] + m_RegionSize[i] >= inputMitk->GetDimension(i)) { m_RegionSize[i] = inputMitk->GetDimension(i) - m_RegionIndex[i]; } } for (unsigned int i = 0; i < 3; ++i) { m_RegionIndex[i] = m_CroppingRegion.GetIndex()[i]; m_RegionSize[i] = m_CroppingRegion.GetSize()[i]; } } else { // Check if a 3D or 4D image is present unsigned int timeSteps = 1; if (inputMitk->GetDimension() == 4) timeSteps = inputMitk->GetDimension(3); ImageType::IndexType minima, maxima; if (inputMitk->GetDimension() == 4) { // initialize with time step 0 m_TimeSelector = mitk::ImageTimeSelector::New(); m_TimeSelector->SetInput(inputMitk); m_TimeSelector->SetTimeNr(0); m_TimeSelector->UpdateLargestPossibleRegion(); inputMitk = m_TimeSelector->GetOutput(); } ImagePointer inputItk = ImageType::New(); mitk::CastToItkImage(inputMitk, inputItk); // it is assumed that all volumes in a time series have the same 3D dimensions ImageType::RegionType origRegion = inputItk->GetLargestPossibleRegion(); // Initialize min and max on the first (or only) time step maxima = inputItk->GetLargestPossibleRegion().GetIndex(); minima[0] = inputItk->GetLargestPossibleRegion().GetSize()[0]; minima[1] = inputItk->GetLargestPossibleRegion().GetSize()[1]; minima[2] = inputItk->GetLargestPossibleRegion().GetSize()[2]; typedef itk::ImageRegionConstIterator ConstIteratorType; for (unsigned int idx = 0; idx < timeSteps; ++idx) { // if 4D image, update time step and itk image if (idx > 0) { m_TimeSelector->SetTimeNr(idx); m_TimeSelector->UpdateLargestPossibleRegion(); inputMitk = m_TimeSelector->GetOutput(); mitk::CastToItkImage(inputMitk, inputItk); } ConstIteratorType inIt(inputItk, origRegion); for (inIt.GoToBegin(); !inIt.IsAtEnd(); ++inIt) { float pix_val = inIt.Get(); if (fabs(pix_val - m_BackgroundValue) > mitk::eps) { for (int i = 0; i < 3; i++) { - minima[i] = vnl_math_min((int)minima[i], (int)(inIt.GetIndex()[i])); - maxima[i] = vnl_math_max((int)maxima[i], (int)(inIt.GetIndex()[i])); + minima[i] = std::min((int)minima[i], (int)(inIt.GetIndex()[i])); + maxima[i] = std::max((int)maxima[i], (int)(inIt.GetIndex()[i])); } } } } typedef ImageType::RegionType::SizeType::SizeValueType SizeValueType; m_RegionSize[0] = (SizeValueType)(m_MarginFactor * (maxima[0] - minima[0] + 1)); m_RegionSize[1] = (SizeValueType)(m_MarginFactor * (maxima[1] - minima[1] + 1)); m_RegionSize[2] = (SizeValueType)(m_MarginFactor * (maxima[2] - minima[2] + 1)); m_RegionIndex = minima; m_RegionIndex[0] -= (m_RegionSize[0] - maxima[0] + minima[0] - 1) / 2; m_RegionIndex[1] -= (m_RegionSize[1] - maxima[1] + minima[1] - 1) / 2; m_RegionIndex[2] -= (m_RegionSize[2] - maxima[2] + minima[2] - 1) / 2; ImageType::RegionType cropRegion(m_RegionIndex, m_RegionSize); origRegion.Crop(cropRegion); m_RegionSize[0] = origRegion.GetSize()[0]; m_RegionSize[1] = origRegion.GetSize()[1]; m_RegionSize[2] = origRegion.GetSize()[2]; m_RegionIndex[0] = origRegion.GetIndex()[0]; m_RegionIndex[1] = origRegion.GetIndex()[1]; m_RegionIndex[2] = origRegion.GetIndex()[2]; m_CroppingRegion = origRegion; } } void mitk::AutoCropImageFilter::GenerateInputRequestedRegion() { } const mitk::PixelType mitk::AutoCropImageFilter::GetOutputPixelType() { return this->GetInput()->GetPixelType(); } void mitk::AutoCropImageFilter::SetCroppingRegion(RegionType overrideRegion) { m_CroppingRegion = overrideRegion; m_OverrideCroppingRegion = true; } diff --git a/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp b/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp index 3a59126040..e34ff3e2c3 100644 --- a/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp +++ b/Modules/AlgorithmsExt/src/mitkCovarianceMatrixCalculator.cpp @@ -1,412 +1,412 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkCovarianceMatrixCalculator.h" #include #include #include #include #include #include #include #include // forward declarations of private functions static vtkIdList *GetNeighboursOfPoint(unsigned int index, vtkPolyData *polydata); static vtkIdList *CalculatePCAonPointNeighboursForNormalVector(int index, double normal[3], itk::Matrix &mat, double curVertex[3], std::vector &pointList, vtkPolyData *polyData); static itk::Matrix ComputeCovarianceMatrix(itk::Matrix &axes, double sigma[3], double normalizationValue); namespace mitk { /** \brief Pimpl to hold the private data in the CovarianceMatrixCalculator.*/ struct CovarianceMatrixCalculatorData { vtkPolyDataNormals *m_PolyDataNormals; vtkPolyData *m_PolyData; Surface *m_Input; double m_VoronoiScalingFactor; bool m_EnableNormalization; double m_MeanVariance; CovarianceMatrixCalculatorData() : m_PolyDataNormals(vtkPolyDataNormals::New()), m_PolyData(nullptr), m_Input(nullptr), m_VoronoiScalingFactor(1.0), m_EnableNormalization(false), m_MeanVariance(0.0) { m_PolyDataNormals->SplittingOff(); } ~CovarianceMatrixCalculatorData() { if (m_PolyDataNormals) m_PolyDataNormals->Delete(); } }; } mitk::CovarianceMatrixCalculator::CovarianceMatrixCalculator() : d(new CovarianceMatrixCalculatorData()) { } mitk::CovarianceMatrixCalculator::~CovarianceMatrixCalculator() { delete d; } void mitk::CovarianceMatrixCalculator::SetVoronoiScalingFator(const double factor) { d->m_VoronoiScalingFactor = factor; } void mitk::CovarianceMatrixCalculator::EnableNormalization(bool state) { d->m_EnableNormalization = state; } double mitk::CovarianceMatrixCalculator::GetMeanVariance() const { return d->m_MeanVariance; } const mitk::CovarianceMatrixCalculator::CovarianceMatrixList &mitk::CovarianceMatrixCalculator::GetCovarianceMatrices() const { return m_CovarianceMatrixList; } void mitk::CovarianceMatrixCalculator::SetInputSurface(Surface *input) { d->m_Input = input; } void mitk::CovarianceMatrixCalculator::ComputeCovarianceMatrices() { double normalizationValue = -1.0; vtkDataArray *normals = nullptr; d->m_MeanVariance = 0.0; if (!d->m_Input) mitkThrow() << "No input surface was set in mitk::CovarianceMatrixCalculator"; d->m_PolyData = d->m_Input->GetVtkPolyData(); // Optional normal calculation can be disabled to use the normals // of the surface: // normals = d->m_PolyData->GetPointData()->GetNormals(); //// compute surface normals if the surface has no normals // if ( normals == nullptr ) //{ d->m_PolyDataNormals->SetInputData(d->m_PolyData); d->m_PolyDataNormals->Update(); normals = d->m_PolyDataNormals->GetOutput()->GetPointData()->GetNormals(); //} if (d->m_EnableNormalization) normalizationValue = 1.5; // clear the matrixlist m_CovarianceMatrixList.clear(); // allocate memory if required if (d->m_PolyData->GetNumberOfPoints() > (vtkIdType)m_CovarianceMatrixList.capacity()) m_CovarianceMatrixList.reserve(d->m_PolyData->GetNumberOfPoints()); for (vtkIdType i = 0; i < d->m_PolyData->GetNumberOfPoints(); ++i) { Vertex normal; Vertex currentVertex; Vertex variances = {0.0, 0.0, 0.0}; CovarianceMatrix mat; mat.Fill(0.0); normals->GetTuple(i, normal); d->m_PolyData->GetPoint(i, currentVertex); ComputeOrthonormalCoordinateSystem(i, normal, mat, variances, currentVertex); // use prefactor for sigma along surface variances[0] = (d->m_VoronoiScalingFactor * variances[0]); variances[1] = (d->m_VoronoiScalingFactor * variances[1]); variances[2] = (d->m_VoronoiScalingFactor * variances[2]); d->m_MeanVariance += (variances[0] + variances[1] + variances[2]); // compute the covariance matrix and save it const CovarianceMatrix covarianceMatrix = ComputeCovarianceMatrix(mat, variances, normalizationValue); m_CovarianceMatrixList.push_back(covarianceMatrix); } if (d->m_EnableNormalization) d->m_MeanVariance = normalizationValue / 3.0; else d->m_MeanVariance /= (3.0 * (double)d->m_PolyData->GetNumberOfPoints()); // reset input d->m_PolyData = nullptr; d->m_Input = nullptr; } // Get a list with the id's of all surrounding conected vertices // to the current vertex at the given index in the polydata vtkIdList *GetNeighboursOfPoint(unsigned int index, vtkPolyData *polydata) { vtkIdList *cellIds = vtkIdList::New(); vtkIdList *result = vtkIdList::New(); polydata->GetPointCells(index, cellIds); for (vtkIdType j = 0; j < cellIds->GetNumberOfIds(); j++) { vtkIdList *newPoints = polydata->GetCell(cellIds->GetId(j))->GetPointIds(); for (vtkIdType k = 0; k < newPoints->GetNumberOfIds(); k++) { // if point has not yet been inserted add id if (result->IsId(newPoints->GetId(k)) == -1) { result->InsertNextId(newPoints->GetId(k)); } } } cellIds->Delete(); return result; } // Computes a primary component analysis of the surounding vertices // of the verex at the current index. vtkIdList *CalculatePCAonPointNeighboursForNormalVector(int index, double normal[3], itk::Matrix &mat, double curVertex[3], std::vector &pointList, vtkPolyData *polyData) { typedef std::vector VectorType; typedef VectorType::const_iterator ConstPointIterator; typedef double Vertex[3]; Vertex mean = {0.0, 0.0, 0.0}; Vertex tmp = {0.0, 0.0, 0.0}; vtkIdList *neighbourPoints = GetNeighboursOfPoint(index, polyData); const vtkIdType size = neighbourPoints->GetNumberOfIds(); // reserve memory for all neighbours pointList.reserve(size); // project neighbours on plane given by normal // and compute mean for (vtkIdType i = 0; i < size; ++i) { mitk::Point3D p; Vertex resultPoint; polyData->GetPoint((neighbourPoints->GetId(i)), tmp); vtkPlane::GeneralizedProjectPoint(tmp, curVertex, normal, resultPoint); p[0] = resultPoint[0]; p[1] = resultPoint[1]; p[2] = resultPoint[2]; mean[0] += p[0]; mean[1] += p[1]; mean[2] += p[2]; pointList.push_back(p); } mean[0] /= (double)size; mean[1] /= (double)size; mean[2] /= (double)size; // compute the covariances with matrix multiplication for (ConstPointIterator it = pointList.begin(); it != pointList.end(); ++it) { tmp[0] = ((*it)[0] - mean[0]); tmp[1] = ((*it)[1] - mean[1]); tmp[2] = ((*it)[2] - mean[2]); // on diagonal elements mat[0][0] += tmp[0] * tmp[0]; mat[1][1] += tmp[1] * tmp[1]; mat[2][2] += tmp[2] * tmp[2]; // of diagonal elements mat[1][0] += tmp[0] * tmp[1]; mat[2][0] += tmp[0] * tmp[2]; mat[2][1] += tmp[1] * tmp[2]; } // copy upper triangle to lower triangle, // we got a symetric matrix mat[0][1] = mat[1][0]; mat[0][2] = mat[2][0]; mat[1][2] = mat[2][1]; // variance mat /= (size - 1); - vnl_svd svd(mat.GetVnlMatrix()); + vnl_svd svd(mat.GetVnlMatrix().as_ref()); for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) mat[i][j] = svd.U()[j][i]; return neighbourPoints; } // Computes an orthonormal system for a vertex with it's surrounding neighbours. void mitk::CovarianceMatrixCalculator::ComputeOrthonormalCoordinateSystem( const int index, Vertex normal, CovarianceMatrix &axes, Vertex variances, Vertex curVertex) { typedef std::vector VectorType; typedef VectorType::const_iterator ConstPointIterator; VectorType projectedPoints; Vertex meanValues = {0.0, 0.0, 0.0}; // project neighbours to new coordinate system and get principal axes vtkIdList *neighbourPoints = CalculatePCAonPointNeighboursForNormalVector(index, normal, axes, curVertex, projectedPoints, d->m_PolyData); // Set the normal as the third principal axis axes[2][0] = normal[0]; axes[2][1] = normal[1]; axes[2][2] = normal[2]; for (vtkIdType i = 0; i < neighbourPoints->GetNumberOfIds(); ++i) { mitk::Point3D projectedPoint; Vertex curNeighbour; d->m_PolyData->GetPoint(neighbourPoints->GetId(i), curNeighbour); curNeighbour[0] = curNeighbour[0] - curVertex[0]; curNeighbour[1] = curNeighbour[1] - curVertex[1]; curNeighbour[2] = curNeighbour[2] - curVertex[2]; for (int k = 0; k < 3; ++k) { projectedPoint[k] = axes[k][0] * curNeighbour[0] + axes[k][1] * curNeighbour[1] + axes[k][2] * curNeighbour[2]; meanValues[k] += projectedPoint[k]; } // reuse the allocated vector from the PCA on the point neighbours projectedPoints[i] = projectedPoint; } meanValues[0] /= (double)projectedPoints.size(); meanValues[1] /= (double)projectedPoints.size(); meanValues[2] /= (double)projectedPoints.size(); // compute variances along new axes for (ConstPointIterator it = projectedPoints.begin(); it != projectedPoints.end(); ++it) { const mitk::Point3D &p = *it; variances[0] += (p[0] - meanValues[0]) * (p[0] - meanValues[0]); variances[1] += (p[1] - meanValues[1]) * (p[1] - meanValues[1]); variances[2] += (p[2] - meanValues[2]) * (p[2] - meanValues[2]); } variances[0] /= (double)(projectedPoints.size() - 1); variances[1] /= (double)(projectedPoints.size() - 1); variances[2] /= (double)(projectedPoints.size() - 1); // clean up neighbourPoints->Delete(); } // Sorts the axes of the computed orthonormal system based on // the eigenvalues in a descending order itk::Matrix ComputeCovarianceMatrix(itk::Matrix &axes, double sigma[3], double normalizationValue) { unsigned int idxMax, idxMin, idxBetween; itk::Matrix returnValue; itk::Matrix V; itk::Matrix diagMatrix; diagMatrix.Fill(0.0); if (sigma[0] >= sigma[1] && sigma[0] >= sigma[2]) { idxMax = 0; if (sigma[1] >= sigma[2]) { idxBetween = 1; idxMin = 2; } else { idxBetween = 2; idxMin = 1; } } else if (sigma[1] >= sigma[0] && sigma[1] >= sigma[2]) { idxMax = 1; if (sigma[0] >= sigma[2]) { idxBetween = 0; idxMin = 2; } else { idxBetween = 2; idxMin = 0; } } else // index 2 corresponds to largest sigma { idxMax = 2; if (sigma[0] >= sigma[1]) { idxBetween = 0; idxMin = 1; } else { idxBetween = 1; idxMin = 0; } } V[0][0] = axes[idxMax][0]; V[1][0] = axes[idxMax][1]; V[2][0] = axes[idxMax][2]; V[0][1] = axes[idxBetween][0]; V[1][1] = axes[idxBetween][1]; V[2][1] = axes[idxBetween][2]; V[0][2] = axes[idxMin][0]; V[1][2] = axes[idxMin][1]; V[2][2] = axes[idxMin][2]; diagMatrix[0][0] = sigma[idxMax]; diagMatrix[1][1] = sigma[idxBetween]; diagMatrix[2][2] = sigma[idxMin]; returnValue = V * diagMatrix * V.GetTranspose(); if (normalizationValue > 0.0) { double trace = returnValue[0][0] + returnValue[1][1] + returnValue[2][2]; returnValue *= (normalizationValue / trace); } return returnValue; } diff --git a/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp index d1c2edf31d..a19cad722b 100644 --- a/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp +++ b/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp @@ -1,158 +1,158 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkCropTimestepsImageFilter.h" #include #include #include #include void mitk::CropTimestepsImageFilter::VerifyInputImage(const mitk::Image* inputImage) const { if (!inputImage->IsInitialized()) mitkThrow() << "Input image is not initialized."; if (!inputImage->IsVolumeSet()) mitkThrow() << "Input image volume is not set."; auto geometry = inputImage->GetGeometry(); if (nullptr == geometry || !geometry->IsValid()) mitkThrow() << "Input image has invalid geometry."; if (inputImage->GetDimension() != 4) { mitkThrow() << "CropTimestepsImageFilter only works with 2D+t and 3D+t images."; } if (inputImage->GetTimeSteps() ==1) { mitkThrow() << "Input image has only one timestep."; } if (!geometry->GetImageGeometry()) mitkThrow() << "Geometry of input image is not an image geometry."; } void mitk::CropTimestepsImageFilter::GenerateOutputInformation() { Image::ConstPointer input = this->GetInput(); Image::Pointer output = this->GetOutput(); if (m_LowerBoundaryTimestep > m_UpperBoundaryTimestep) { mitkThrow() << "lower timestep is larger than upper timestep."; } if (m_UpperBoundaryTimestep == std::numeric_limits::max()) { m_UpperBoundaryTimestep = input->GetTimeSteps(); } else if (m_UpperBoundaryTimestep > input->GetTimeSteps()) { m_UpperBoundaryTimestep = input->GetTimeSteps(); MITK_WARN << "upper boundary timestep set to " << m_UpperBoundaryTimestep; } m_DesiredRegion = ComputeDesiredRegion(); unsigned int dimension = input->GetDimension(); auto dimensions = new unsigned int[dimension]; itk2vtk(m_DesiredRegion.GetSize(), dimensions); if (dimension > 3) memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3) * sizeof(unsigned int)); dimensions[3] = m_UpperBoundaryTimestep - m_LowerBoundaryTimestep; // create basic slicedGeometry that will be initialized below output->Initialize(mitk::PixelType(input->GetPixelType()), dimension, dimensions); delete[] dimensions; auto newTimeGeometry = AdaptTimeGeometry(input->GetTimeGeometry(), m_LowerBoundaryTimestep, m_UpperBoundaryTimestep); output->SetTimeGeometry(newTimeGeometry); output->SetPropertyList(input->GetPropertyList()); } mitk::SlicedData::RegionType mitk::CropTimestepsImageFilter::ComputeDesiredRegion() const { auto desiredRegion = this->GetInput()->GetLargestPossibleRegion(); auto index = desiredRegion.GetIndex(); auto size = desiredRegion.GetSize(); unsigned int timeDimension = 3; index[timeDimension] = m_LowerBoundaryTimestep; size[timeDimension] = m_UpperBoundaryTimestep - m_LowerBoundaryTimestep; desiredRegion.SetIndex(index); desiredRegion.SetSize(size); return desiredRegion; } mitk::TimeGeometry::Pointer mitk::CropTimestepsImageFilter::AdaptTimeGeometry(mitk::TimeGeometry::ConstPointer sourceGeometry, unsigned int startTimestep, unsigned int endTimestep) const { auto newTimeGeometry = mitk::ArbitraryTimeGeometry::New(); newTimeGeometry->ClearAllGeometries(); for (unsigned int timestep = startTimestep; timestep < endTimestep; timestep++) { auto geometryForTimePoint = sourceGeometry->GetGeometryForTimeStep(timestep); auto minTP = sourceGeometry->GetMinimumTimePoint(timestep); auto maxTP = sourceGeometry->GetMaximumTimePoint(timestep); /////////////////////////////////////// // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. // This workarround should be removed as soon as T28262 is solved! if (timestep + 1 == sourceGeometry->CountTimeSteps() && minTP == maxTP) { maxTP = minTP + 1.; } // End of workarround for T27883 ////////////////////////////////////// newTimeGeometry->AppendNewTimeStepClone(geometryForTimePoint, minTP, maxTP); } return newTimeGeometry.GetPointer(); } void mitk::CropTimestepsImageFilter::GenerateData() { const auto* inputImage = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized() == false)) return; auto timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(inputImage); unsigned int timeStart = m_DesiredRegion.GetIndex(3); unsigned int timeEnd = timeStart + m_DesiredRegion.GetSize(3); for (unsigned int timestep = timeStart; timestep < timeEnd; ++timestep) { timeSelector->SetTimeNr(timestep); timeSelector->UpdateLargestPossibleRegion(); mitk::ImageReadAccessor imageAccessorWithOneTimestep(timeSelector->GetOutput()); output->SetVolume(imageAccessorWithOneTimestep.GetData(), timestep-timeStart); } } void mitk::CropTimestepsImageFilter::SetInput(const InputImageType* image) { if (this->GetInput() == image) return; Superclass::SetInput(image); } void mitk::CropTimestepsImageFilter::SetInput(unsigned int index, const InputImageType* image) { if (0 != index) mitkThrow() << "Input index " << index << " is invalid."; this->SetInput(image); } -void mitk::CropTimestepsImageFilter::VerifyInputInformation() +void mitk::CropTimestepsImageFilter::VerifyInputInformation() const { Superclass::VerifyInputInformation(); VerifyInputImage(this->GetInput()); } diff --git a/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp index d08b3c1ddc..c4d13bff6c 100644 --- a/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp +++ b/Modules/AlgorithmsExt/src/mitkMaskImageFilter.cpp @@ -1,241 +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. ============================================================================*/ #include "mitkMaskImageFilter.h" #include "mitkImageTimeSelector.h" #include "mitkProperties.h" #include "mitkTimeHelper.h" #include "mitkImageAccessByItk.h" #include "mitkImageToItk.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIteratorWithIndex.h" #include mitk::MaskImageFilter::MaskImageFilter() : m_Mask(nullptr) { this->SetNumberOfIndexedInputs(2); this->SetNumberOfRequiredInputs(2); m_InputTimeSelector = mitk::ImageTimeSelector::New(); m_MaskTimeSelector = mitk::ImageTimeSelector::New(); m_OutputTimeSelector = mitk::ImageTimeSelector::New(); m_OverrideOutsideValue = false; m_OutsideValue = 0; } mitk::MaskImageFilter::~MaskImageFilter() { } void mitk::MaskImageFilter::SetMask(const mitk::Image *mask) { // Process object is not const-correct so the const_cast is required here m_Mask = const_cast(mask); this->ProcessObject::SetNthInput(1, m_Mask); } const mitk::Image *mitk::MaskImageFilter::GetMask() const { return m_Mask; } void mitk::MaskImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Image *output = this->GetOutput(); mitk::Image *input = this->GetInput(); mitk::Image *mask = m_Mask; if ((output->IsInitialized() == false) || (mask == nullptr) || (mask->GetTimeGeometry()->CountTimeSteps() == 0)) return; input->SetRequestedRegionToLargestPossibleRegion(); mask->SetRequestedRegionToLargestPossibleRegion(); GenerateTimeInInputRegion(output, input); GenerateTimeInInputRegion(output, mask); } void mitk::MaskImageFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; itkDebugMacro(<< "GenerateOutputInformation()"); output->Initialize(input->GetPixelType(), *input->GetTimeGeometry()); output->SetPropertyList(input->GetPropertyList()->Clone()); m_TimeOfHeaderInitialization.Modified(); } template void mitk::MaskImageFilter::InternalComputeMask(itk::Image *inputItkImage) { // dirty quick fix, duplicating code so both unsigned char and unsigned short are supported // this should be changed once unsigned char segmentations can be converted to unsigned short mitk::PixelType pixelType = m_MaskTimeSelector->GetOutput()->GetImageDescriptor()->GetChannelDescriptor().GetPixelType(); - if (pixelType.GetComponentType() == itk::ImageIOBase::UCHAR) + if (pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR) { typedef itk::Image ItkInputImageType; typedef itk::Image ItkMaskImageType; typedef itk::Image ItkOutputImageType; typedef itk::ImageRegionConstIterator ItkInputImageIteratorType; typedef itk::ImageRegionConstIterator ItkMaskImageIteratorType; typedef itk::ImageRegionIteratorWithIndex ItkOutputImageIteratorType; typename mitk::ImageToItk::Pointer maskimagetoitk = mitk::ImageToItk::New(); maskimagetoitk->SetInput(m_MaskTimeSelector->GetOutput()); maskimagetoitk->Update(); typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput(); typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); outputimagetoitk->SetInput(m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); // create the iterators typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); ItkMaskImageIteratorType maskIt(maskItkImage, inputRegionOfInterest); ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest); // typename ItkOutputImageType::PixelType outsideValue = itk::NumericTraits::min(); if (!m_OverrideOutsideValue) m_OutsideValue = itk::NumericTraits::min(); m_MinValue = std::numeric_limits::max(); m_MaxValue = std::numeric_limits::min(); for (inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd(); ++inputIt, ++maskIt, ++outputIt) { if (maskIt.Get() > itk::NumericTraits::Zero) { outputIt.Set(inputIt.Get()); - m_MinValue = vnl_math_min((float)inputIt.Get(), (float)m_MinValue); - m_MaxValue = vnl_math_max((float)inputIt.Get(), (float)m_MaxValue); + m_MinValue = std::min((float)inputIt.Get(), (float)m_MinValue); + m_MaxValue = std::max((float)inputIt.Get(), (float)m_MaxValue); } else { outputIt.Set(m_OutsideValue); } } } else { { typedef itk::Image ItkInputImageType; typedef itk::Image ItkMaskImageType; typedef itk::Image ItkOutputImageType; typedef itk::ImageRegionConstIterator ItkInputImageIteratorType; typedef itk::ImageRegionConstIterator ItkMaskImageIteratorType; typedef itk::ImageRegionIteratorWithIndex ItkOutputImageIteratorType; typename mitk::ImageToItk::Pointer maskimagetoitk = mitk::ImageToItk::New(); maskimagetoitk->SetInput(m_MaskTimeSelector->GetOutput()); maskimagetoitk->Update(); typename ItkMaskImageType::Pointer maskItkImage = maskimagetoitk->GetOutput(); typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); outputimagetoitk->SetInput(m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); // create the iterators typename ItkInputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); ItkMaskImageIteratorType maskIt(maskItkImage, inputRegionOfInterest); ItkOutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest); // typename ItkOutputImageType::PixelType outsideValue = itk::NumericTraits::min(); if (!m_OverrideOutsideValue) m_OutsideValue = itk::NumericTraits::min(); m_MinValue = std::numeric_limits::max(); m_MaxValue = std::numeric_limits::min(); for (inputIt.GoToBegin(), maskIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd() && !maskIt.IsAtEnd(); ++inputIt, ++maskIt, ++outputIt) { if (maskIt.Get() > itk::NumericTraits::Zero) { outputIt.Set(inputIt.Get()); - m_MinValue = vnl_math_min((float)inputIt.Get(), (float)m_MinValue); - m_MaxValue = vnl_math_max((float)inputIt.Get(), (float)m_MaxValue); + m_MinValue = std::min((float)inputIt.Get(), (float)m_MinValue); + m_MaxValue = std::max((float)inputIt.Get(), (float)m_MaxValue); } else { outputIt.Set(m_OutsideValue); } } } } } void mitk::MaskImageFilter::GenerateData() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer mask = m_Mask; mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized() == false) || (mask.IsNull()) || (mask->GetTimeGeometry()->CountTimeSteps() == 0)) return; m_InputTimeSelector->SetInput(input); m_MaskTimeSelector->SetInput(mask); m_OutputTimeSelector->SetInput(this->GetOutput()); mitk::Image::RegionType outputRegion = output->GetRequestedRegion(); const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); const mitk::TimeGeometry *maskTimeGeometry = mask->GetTimeGeometry(); ScalarType timeInMS; int timestep = 0; int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); int t; for (t = tstart; t < tmax; ++t) { timeInMS = outputTimeGeometry->TimeStepToTimePoint(t); timestep = inputTimeGeometry->TimePointToTimeStep(timeInMS); m_InputTimeSelector->SetTimeNr(timestep); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr(t); m_OutputTimeSelector->UpdateLargestPossibleRegion(); timestep = maskTimeGeometry->TimePointToTimeStep(timeInMS); m_MaskTimeSelector->SetTimeNr(timestep); m_MaskTimeSelector->UpdateLargestPossibleRegion(); AccessByItk(m_InputTimeSelector->GetOutput(), InternalComputeMask); } m_TimeOfHeaderInitialization.Modified(); } diff --git a/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp b/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp index ea6eb1135d..7459b1e4ac 100644 --- a/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp +++ b/Modules/AlgorithmsExt/src/mitkNonBlockingAlgorithm.cpp @@ -1,203 +1,189 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkNonBlockingAlgorithm.h" #include "mitkCallbackFromGUIThread.h" #include "mitkDataStorage.h" #include namespace mitk { - NonBlockingAlgorithm::NonBlockingAlgorithm() : m_ThreadID(-1), m_UpdateRequests(0), m_KillRequest(false) + NonBlockingAlgorithm::NonBlockingAlgorithm() : m_UpdateRequests(0), m_KillRequest(false) { - m_ParameterListMutex = itk::FastMutexLock::New(); m_Parameters = PropertyList::New(); - m_MultiThreader = itk::MultiThreader::New(); } - NonBlockingAlgorithm::~NonBlockingAlgorithm() {} + NonBlockingAlgorithm::~NonBlockingAlgorithm() + { + if (m_Thread.joinable()) + m_Thread.join(); + } + void mitk::NonBlockingAlgorithm::SetDataStorage(DataStorage &storage) { m_DataStorage = &storage; } DataStorage *mitk::NonBlockingAlgorithm::GetDataStorage() { return m_DataStorage.Lock(); } void NonBlockingAlgorithm::Initialize(const NonBlockingAlgorithm *itkNotUsed(other)) { // define one input, one output basedata object // some basedata input - image, surface, whatever BaseData::Pointer input; SetPointerParameter("Input", input); // some basedata output BaseData::Pointer output; SetPointerParameter("Output", output); } void NonBlockingAlgorithm::SetPointerParameter(const char *parameter, BaseData *value) { - m_ParameterListMutex->Lock(); + m_ParameterListMutex.lock(); m_Parameters->SetProperty(parameter, SmartPointerProperty::New(value)); - m_ParameterListMutex->Unlock(); + m_ParameterListMutex.unlock(); } void NonBlockingAlgorithm::DefineTriggerParameter(const char *parameter) { BaseProperty *value = m_Parameters->GetProperty(parameter); if (value && m_TriggerPropertyConnections.find(parameter) == m_TriggerPropertyConnections.end()) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &NonBlockingAlgorithm::TriggerParameterModified); m_TriggerPropertyConnections[parameter] = value->AddObserver(itk::ModifiedEvent(), command); } } void NonBlockingAlgorithm::UnDefineTriggerParameter(const char *parameter) { auto iter = m_TriggerPropertyConnections.find(parameter); if (iter != m_TriggerPropertyConnections.end()) { BaseProperty *value = m_Parameters->GetProperty(parameter); MITK_ERROR(!value) << "NonBlockingAlgorithm::UnDefineTriggerProperty() in bad state." << std::endl; ; value->RemoveObserver(m_TriggerPropertyConnections[parameter]); m_TriggerPropertyConnections.erase(iter); } } void NonBlockingAlgorithm::Reset() { Initialize(); } void NonBlockingAlgorithm::StartBlockingAlgorithm() { StartAlgorithm(); StopAlgorithm(); } void NonBlockingAlgorithm::StartAlgorithm() { if (!ReadyToRun()) return; // let algorithm check if all input/parameters are ok if (m_KillRequest) return; // someone wants us to die - m_ParameterListMutex->Lock(); + m_ParameterListMutex.lock(); m_ThreadParameters.m_Algorithm = this; ++m_UpdateRequests; - m_ParameterListMutex->Unlock(); - if (m_ThreadID != -1) // thread already running. But something obviously wants us to recalculate the output + m_ParameterListMutex.unlock(); + if (m_Thread.joinable()) // thread already running. But something obviously wants us to recalculate the output { return; // thread already running } // spawn a thread that calls ThreadedUpdateFunction(), and ThreadedUpdateFinished() on us - itk::ThreadFunctionType fpointer = &StaticNonBlockingAlgorithmThread; - m_ThreadID = m_MultiThreader->SpawnThread(fpointer, &m_ThreadParameters); + m_Thread = std::thread(StaticNonBlockingAlgorithmThread, &m_ThreadParameters); } void NonBlockingAlgorithm::StopAlgorithm() { - if (m_ThreadID == -1) - return; // thread not running - - m_MultiThreader->TerminateThread(m_ThreadID); // waits for the thread to terminate on its own + if (m_Thread.joinable()) + m_Thread.join(); // waits for the thread to terminate on its own } // a static function to call a member of NonBlockingAlgorithm from inside an ITK thread - ITK_THREAD_RETURN_TYPE NonBlockingAlgorithm::StaticNonBlockingAlgorithmThread(void *param) + itk::ITK_THREAD_RETURN_TYPE NonBlockingAlgorithm::StaticNonBlockingAlgorithmThread(ThreadParameters *param) { - // itk::MultiThreader provides an itk::MultiThreader::ThreadInfoStruct as parameter - auto *itkmttis = static_cast(param); - - // we need the UserData part of that structure - auto *flsp = static_cast(itkmttis->UserData); - - NonBlockingAlgorithm::Pointer algorithm = flsp->m_Algorithm; + NonBlockingAlgorithm::Pointer algorithm = param->m_Algorithm; // this UserData tells us, which BubbleTool's method to call if (!algorithm) { - return ITK_THREAD_RETURN_VALUE; + return itk::ITK_THREAD_RETURN_DEFAULT_VALUE; } - algorithm->m_ParameterListMutex->Lock(); + algorithm->m_ParameterListMutex.lock(); while (algorithm->m_UpdateRequests > 0) { algorithm->m_UpdateRequests = 0; - algorithm->m_ParameterListMutex->Unlock(); + algorithm->m_ParameterListMutex.unlock(); // actually call the methods that do the work if (algorithm->ThreadedUpdateFunction()) // returns a bool for success/failure { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(algorithm, &NonBlockingAlgorithm::ThreadedUpdateSuccessful); CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command); // algorithm->ThreadedUpdateSuccessful(); } else { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(algorithm, &NonBlockingAlgorithm::ThreadedUpdateFailed); CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread(command); // algorithm->ThreadedUpdateFailed(); } - algorithm->m_ParameterListMutex->Lock(); + algorithm->m_ParameterListMutex.lock(); } - algorithm->m_ParameterListMutex->Unlock(); + algorithm->m_ParameterListMutex.unlock(); - return ITK_THREAD_RETURN_VALUE; + return ITK_THREAD_RETURN_DEFAULT_VALUE; } void NonBlockingAlgorithm::TriggerParameterModified(const itk::EventObject &) { StartAlgorithm(); } bool NonBlockingAlgorithm::ReadyToRun() { return true; // default is always ready } bool NonBlockingAlgorithm::ThreadedUpdateFunction() { return true; } // called from gui thread void NonBlockingAlgorithm::ThreadedUpdateSuccessful(const itk::EventObject &) { ThreadedUpdateSuccessful(); - - m_ParameterListMutex->Lock(); - m_ThreadID = -1; // tested before starting - m_ParameterListMutex->Unlock(); m_ThreadParameters.m_Algorithm = nullptr; } void NonBlockingAlgorithm::ThreadedUpdateSuccessful() { // notify observers that a result is ready InvokeEvent(ResultAvailable(this)); } // called from gui thread void NonBlockingAlgorithm::ThreadedUpdateFailed(const itk::EventObject &) { ThreadedUpdateFailed(); - - m_ParameterListMutex->Lock(); - m_ThreadID = -1; // tested before starting - m_ParameterListMutex->Unlock(); m_ThreadParameters.m_Algorithm = nullptr; // delete } void NonBlockingAlgorithm::ThreadedUpdateFailed() { // notify observers that something went wrong InvokeEvent(ProcessingError(this)); } } // namespace diff --git a/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp b/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp index 3bf7846bc8..dd617de6f5 100644 --- a/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp +++ b/Modules/AlgorithmsExt/src/mitkPlaneFit.cpp @@ -1,193 +1,192 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkPlaneFit.h" #include "mitkGeometryData.h" #include "mitkPlaneGeometry.h" #include -#include #include mitk::PlaneFit::PlaneFit() : m_PointSet(nullptr) { m_TimeGeometry = mitk::ProportionalTimeGeometry::New(); } mitk::PlaneFit::~PlaneFit() { } void mitk::PlaneFit::GenerateOutputInformation() { mitk::PointSet::ConstPointer input = this->GetInput(); mitk::GeometryData::Pointer output = this->GetOutput(); itkDebugMacro(<< "GenerateOutputInformation()"); if (input.IsNull()) return; if (m_PointSet == nullptr) { return; } bool update = false; if (output->GetGeometry() == nullptr || output->GetTimeGeometry() == nullptr) update = true; if ((!update) && (output->GetTimeGeometry()->CountTimeSteps() != input->GetTimeGeometry()->CountTimeSteps())) update = true; if (update) { mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); ProportionalTimeGeometry::Pointer timeGeometry = dynamic_cast(m_TimeGeometry.GetPointer()); timeGeometry->Initialize(planeGeometry, m_PointSet->GetPointSetSeriesSize()); // m_TimeGeometry->InitializeEvenlyTimed( // planeGeometry, m_PointSet->GetPointSetSeriesSize() ); TimeStepType timeStep; for (timeStep = 0; (timeStep < m_PointSet->GetPointSetSeriesSize()) && (timeStep < m_Planes.size()); ++timeStep) { timeGeometry->SetTimeStepGeometry(m_Planes[timeStep], timeStep); } output->SetTimeGeometry(m_TimeGeometry); } } void mitk::PlaneFit::GenerateData() { unsigned int t; for (t = 0; t < m_PointSet->GetPointSetSeriesSize(); ++t) { // check number of data points - less then 3points isn't enough if (m_PointSet->GetSize(t) >= 3) { this->CalculateCentroid(t); this->ProcessPointSet(t); this->InitializePlane(t); } } } void mitk::PlaneFit::SetInput(const mitk::PointSet *pointSet) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(0, const_cast(pointSet)); m_PointSet = pointSet; unsigned int pointSetSize = pointSet->GetPointSetSeriesSize(); m_Planes.resize(pointSetSize); m_Centroids.resize(pointSetSize); m_PlaneVectors.resize(pointSetSize); unsigned int t; for (t = 0; t < pointSetSize; ++t) { m_Planes[t] = mitk::PlaneGeometry::New(); } } const mitk::PointSet *mitk::PlaneFit::GetInput() { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast(this->ProcessObject::GetInput(0)); } void mitk::PlaneFit::CalculateCentroid(int t) { if (m_PointSet == nullptr) return; int ps_total = m_PointSet->GetSize(t); m_Centroids[t][0] = m_Centroids[t][1] = m_Centroids[t][2] = 0.0; for (int i = 0; i < ps_total; i++) { mitk::Point3D p3d = m_PointSet->GetPoint(i, t); m_Centroids[t][0] += p3d[0]; m_Centroids[t][1] += p3d[1]; m_Centroids[t][2] += p3d[2]; } // calculation of centroid m_Centroids[t][0] /= ps_total; m_Centroids[t][1] /= ps_total; m_Centroids[t][2] /= ps_total; } void mitk::PlaneFit::ProcessPointSet(int t) { if (m_PointSet == nullptr) return; // int matrix with POINTS x (X,Y,Z) vnl_matrix dataM(m_PointSet->GetSize(t), 3); int ps_total = m_PointSet->GetSize(t); for (int i = 0; i < ps_total; i++) { mitk::Point3D p3d = m_PointSet->GetPoint(i, t); dataM[i][0] = p3d[0] - m_Centroids[t][0]; dataM[i][1] = p3d[1] - m_Centroids[t][1]; dataM[i][2] = p3d[2] - m_Centroids[t][2]; } // process the SVD (singular value decomposition) from ITK // the vector will be orderd descending vnl_svd svd(dataM, 0.0); // calculate the SVD of A vnl_vector v = svd.nullvector(); // Avoid erratic normal sign switching when the plane changes minimally // by negating the vector for negative x values. if (v[0] < 0) { v = -v; } m_PlaneVectors[t][0] = v[0]; m_PlaneVectors[t][1] = v[1]; m_PlaneVectors[t][2] = v[2]; } mitk::PlaneGeometry::Pointer mitk::PlaneFit::GetPlaneGeometry(int t) { return m_Planes[t]; } const mitk::Vector3D &mitk::PlaneFit::GetPlaneNormal(int t) const { return m_PlaneVectors[t]; } const mitk::Point3D &mitk::PlaneFit::GetCentroid(int t) const { return m_Centroids[t]; } void mitk::PlaneFit::InitializePlane(int t) { m_Planes[t]->InitializePlane(m_Centroids[t], m_PlaneVectors[t]); } diff --git a/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp b/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp index 3615e69a1a..1174ac545d 100644 --- a/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp +++ b/Modules/AlgorithmsExt/src/mitkSimpleHistogram.cpp @@ -1,311 +1,311 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkSimpleHistogram.h" #include "mitkImageReadAccessor.h" #include "mitkSimpleUnstructuredGridHistogram.h" #include "mitkUnstructuredGrid.h" namespace mitk { void SimpleImageHistogram::ComputeFromBaseData(BaseData *src) { valid = false; // check if input is valid if (src == nullptr) return; auto *source = dynamic_cast(src); if (source == nullptr) return; else if (source->IsEmpty()) return; // dummy histogram { min = 0; max = 1; first = 0; last = 1; } { int typInt = 0; { - const int typ = source->GetPixelType().GetComponentType(); - if (typ == itk::ImageIOBase::UCHAR) + auto typ = source->GetPixelType().GetComponentType(); + if (typ == itk::IOComponentEnum::UCHAR) typInt = 0; - else if (typ == itk::ImageIOBase::CHAR) + else if (typ == itk::IOComponentEnum::CHAR) typInt = 1; - else if (typ == itk::ImageIOBase::USHORT) + else if (typ == itk::IOComponentEnum::USHORT) typInt = 2; - else if (typ == itk::ImageIOBase::SHORT) + else if (typ == itk::IOComponentEnum::SHORT) typInt = 3; - else if (typ == itk::ImageIOBase::INT) + else if (typ == itk::IOComponentEnum::INT) typInt = 4; - else if (typ == itk::ImageIOBase::UINT) + else if (typ == itk::IOComponentEnum::UINT) typInt = 5; - else if (typ == itk::ImageIOBase::LONG) + else if (typ == itk::IOComponentEnum::LONG) typInt = 6; - else if (typ == itk::ImageIOBase::ULONG) + else if (typ == itk::IOComponentEnum::ULONG) typInt = 7; - else if (typ == itk::ImageIOBase::FLOAT) + else if (typ == itk::IOComponentEnum::FLOAT) typInt = 8; - else if (typ == itk::ImageIOBase::DOUBLE) + else if (typ == itk::IOComponentEnum::DOUBLE) typInt = 9; else { MITK_WARN << "Pixel type not supported by SimpleImageHistogram"; return; } } first = -32768; last = 65535; // support at least full signed and unsigned short range if (histogram) delete histogram; histogram = new CountType[last - first + 1]; memset(histogram, 0, sizeof(CountType) * (last - first + 1)); highest = 0; max = first - 1; min = last + 1; unsigned int num = 1; for (unsigned int r = 0; r < source->GetDimension(); r++) num *= source->GetDimension(r); // MITK_INFO << "building histogramm of integer image: 0=" << source->GetDimension(0) << " 1=" << // source->GetDimension(1) << " 2=" << source->GetDimension(2) << " 3=" << source->GetDimension(3); ImageReadAccessor sourceAcc(source); const void *src = sourceAcc.GetData(); do { int value = 0; switch (typInt) { case 0: { auto *t = (unsigned char *)src; value = *t++; src = (void *)t; } break; case 1: { auto *t = (signed char *)src; value = *t++; src = (void *)t; } break; case 2: { auto *t = (unsigned short *)src; value = *t++; src = (void *)t; } break; case 3: { auto *t = (signed short *)src; value = *t++; src = (void *)t; } break; case 4: { auto *t = (signed int *)src; value = *t++; src = (void *)t; } break; case 5: { auto *t = (unsigned int *)src; value = *t++; src = (void *)t; } break; case 6: { auto *t = (signed long *)src; value = *t++; src = (void *)t; } break; case 7: { auto *t = (unsigned long *)src; value = *t++; src = (void *)t; } break; case 8: { auto *t = (float *)src; value = *t++; src = (void *)t; } break; case 9: { auto *t = (double *)src; value = *t++; src = (void *)t; } break; } if (value >= first && value <= last) { if (value < min) min = value; if (value > max) max = value; CountType tmp = ++histogram[value - first]; if (tmp > highest) highest = tmp; } } while (--num); MITK_INFO << "histogramm computed: min=" << min << " max=" << max << " highestBin=" << highest << " samples=" << num; } invLogHighest = 1.0 / log(double(highest)); valid = true; } bool SimpleImageHistogram::GetValid() { return valid; } float SimpleImageHistogram::GetRelativeBin(double left, double right) const { if (!valid) return 0.0f; int iLeft = floorf(left); int iRight = ceilf(right); /* double sum = 0; for( int r = 0 ; r < 256 ; r++) { int pos = left + (right-left) * r/255.0; int posInArray = floorf(pos+0.5f) - first; sum += float(log(double(histogram[posInArray]))); } sum /= 256.0; return float(sum*invLogHighest); */ CountType maximum = 0; for (int i = iLeft; i <= iRight; i++) { int posInArray = i - first; if (histogram[posInArray] > maximum) maximum = histogram[posInArray]; } return float(log(double(maximum)) * invLogHighest); } class ImageHistogramCacheElement : public SimpleHistogramCache::Element { public: void ComputeFromBaseData(BaseData *baseData) override { histogram.ComputeFromBaseData(baseData); } SimpleHistogram *GetHistogram() override { return &histogram; } SimpleImageHistogram histogram; }; class UnstructuredGridHistogramCacheElement : public SimpleHistogramCache::Element { public: void ComputeFromBaseData(BaseData *baseData) override { histogram.ComputeFromBaseData(baseData); } SimpleHistogram *GetHistogram() override { return &histogram; } SimpleUnstructuredGridHistogram histogram; }; SimpleHistogram *SimpleHistogramCache::operator[](BaseData::Pointer sp_BaseData) { BaseData *p_BaseData = sp_BaseData.GetPointer(); if (!p_BaseData) { MITK_WARN << "SimpleHistogramCache::operator[] with null base data called"; return nullptr; } Element *elementToUpdate = nullptr; bool first = true; for (auto iter = cache.begin(); iter != cache.end(); iter++) { Element *e = *iter; BaseData *p_tmp = e->baseData.Lock(); if (p_tmp == p_BaseData) { if (!first) { cache.erase(iter); cache.push_front(e); } if (p_BaseData->GetMTime() > e->m_LastUpdateTime.GetMTime()) { elementToUpdate = e; goto recomputeElement; } // MITK_INFO << "using a cached histogram"; return e->GetHistogram(); } first = false; } if (dynamic_cast(p_BaseData)) { elementToUpdate = new ImageHistogramCacheElement(); } else if (dynamic_cast(p_BaseData)) { elementToUpdate = new UnstructuredGridHistogramCacheElement(); } else { MITK_WARN << "not supported: " << p_BaseData->GetNameOfClass(); } elementToUpdate->baseData = p_BaseData; cache.push_front(elementToUpdate); TrimCache(); recomputeElement: // MITK_INFO << "computing a new histogram"; elementToUpdate->ComputeFromBaseData(p_BaseData); elementToUpdate->m_LastUpdateTime.Modified(); return elementToUpdate->GetHistogram(); } SimpleHistogramCache::Element::~Element() {} } diff --git a/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp b/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp index ce6f229ea5..d1ceab8092 100644 --- a/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp +++ b/Modules/AlgorithmsExt/src/mitkWeightedPointTransform.cpp @@ -1,459 +1,459 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkWeightedPointTransform.h" #include "mitkAnisotropicRegistrationCommon.h" #include #include #include typedef itk::Matrix Matrix3x3; typedef std::vector Matrix3x3List; /////////////////////////////////////////////// // forward declarations of private functions /////////////////////////////////////////////// static double ComputeWeightedFRE(vtkPoints *X, vtkPoints *Y, const Matrix3x3List &CovarianceMatricesMoving, const Matrix3x3List &CovarianceMatricesFixed, double FRENormalizationFactor, Matrix3x3List &WeightMatrices, const Matrix3x3 &rotation, const itk::Vector &translation); static void calculateWeightMatrices(const Matrix3x3List &X, const Matrix3x3List &Y, Matrix3x3List &result, const Matrix3x3 &rotation); static void IsotropicRegistration(vtkPoints *X, vtkPoints *Y, vtkLandmarkTransform *landmarkTransform, Matrix3x3 &rotation, itk::Vector &translation); mitk::WeightedPointTransform::WeightedPointTransform() : m_Threshold(1.0e-4), m_MaxIterations(1000), m_Iterations(-1), m_FRE(-1.0), m_FRENormalizationFactor(1.0), m_LandmarkTransform(vtkSmartPointer::New()) { } mitk::WeightedPointTransform::~WeightedPointTransform() { m_FixedPointSet = nullptr; m_MovingPointSet = nullptr; m_LandmarkTransform = nullptr; } void mitk::WeightedPointTransform::ComputeTransformation() { WeightedPointRegister(m_MovingPointSet, m_FixedPointSet, m_CovarianceMatricesMoving, m_CovarianceMatricesFixed, m_Threshold, m_MaxIterations, m_Rotation, m_Translation, m_FRE, m_Iterations); } // computes the weightmatrix with 2 covariance matrices // and a given transformation void calculateWeightMatrices(const Matrix3x3List &X, const Matrix3x3List &Y, Matrix3x3List &result, const Matrix3x3 &rotation) { const vnl_matrix_fixed rotation_T = rotation.GetTranspose(); #pragma omp parallel for for (int i = 0; i < static_cast(X.size()); ++i) { const Matrix3x3 w = rotation * X[i] * rotation_T; result[i] = mitk::AnisotropicRegistrationCommon::CalculateWeightMatrix(w, Y[i]); } } // computes the weighted fiducial registration error double ComputeWeightedFRE(vtkPoints *X, vtkPoints *Y, const Matrix3x3List &CovarianceMatricesMoving, const Matrix3x3List &CovarianceMatricesFixed, double FRENormalizationFactor, Matrix3x3List &WeightMatrices, const Matrix3x3 &rotation, const itk::Vector &translation) { double FRE = 0; // compute weighting matrices calculateWeightMatrices(CovarianceMatricesMoving, CovarianceMatricesFixed, WeightMatrices, rotation); #pragma omp parallel for for (int i = 0; i < static_cast(WeightMatrices.size()); ++i) { // convert to itk data types (nessecary since itk 4 migration) itk::Vector converted_MovingPoint; double point[3]; X->GetPoint(i, point); converted_MovingPoint[0] = point[0]; converted_MovingPoint[1] = point[1]; converted_MovingPoint[2] = point[2]; // transform point itk::Vector p = rotation * converted_MovingPoint + translation; Y->GetPoint(i, point); p[0] -= point[0]; p[1] -= point[1]; p[2] -= point[2]; // do calculation const itk::Vector D = WeightMatrices.at(i) * p; #pragma omp critical FRE += (D[0] * D[0] + D[1] * D[1] + D[2] * D[2]); } FRE /= WeightMatrices.size(); FRE = FRENormalizationFactor * sqrt(FRE); return FRE; } // registers two pointsets with an isotropic landmark transform void IsotropicRegistration(vtkPoints *X, vtkPoints *Y, vtkLandmarkTransform *landmarkTransform, Matrix3x3 &rotation, itk::Vector &translation) { landmarkTransform->SetSourceLandmarks(X); landmarkTransform->SetTargetLandmarks(Y); landmarkTransform->SetModeToRigidBody(); landmarkTransform->Modified(); landmarkTransform->Update(); vtkMatrix4x4 *m = landmarkTransform->GetMatrix(); for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) rotation[i][j] = m->GetElement(i, j); translation[0] = m->GetElement(0, 3); translation[1] = m->GetElement(1, 3); translation[2] = m->GetElement(2, 3); } void mitk::WeightedPointTransform::C_maker(vtkPoints *X, const WeightMatrixList &W, itk::VariableSizeMatrix &returnValue) { #pragma omp parallel for for (int i = 0; i < X->GetNumberOfPoints(); ++i) { unsigned int index = 3u * i; double point[3]; X->GetPoint(i, point); for (int j = 0; j < 3; ++j) { returnValue[index][0] = -W.at(i)[j][1] * point[2] + W.at(i)[j][2] * point[1]; returnValue[index][1] = W.at(i)[j][0] * point[2] - W.at(i)[j][2] * point[0]; returnValue[index][2] = -W.at(i)[j][0] * point[1] + W.at(i)[j][1] * point[0]; returnValue[index][3] = W.at(i)[j][0]; returnValue[index][4] = W.at(i)[j][1]; returnValue[index][5] = W.at(i)[j][2]; index += 1; } } } void mitk::WeightedPointTransform::E_maker(vtkPoints *X, vtkPoints *Y, const WeightMatrixList &W, vnl_vector &returnValue) { #pragma omp parallel for for (int i = 0; i < X->GetNumberOfPoints(); ++i) { unsigned int index = 3u * i; double pX[3]; double pY[3]; Matrix3x3 M; X->GetPoint(i, pX); Y->GetPoint(i, pY); M[0][0] = pY[0] - pX[0]; M[0][1] = pY[1] - pX[1]; M[0][2] = pY[2] - pX[2]; M[1][0] = M[0][0]; M[1][1] = M[0][1]; M[1][2] = M[0][2]; M[2][0] = M[0][0]; M[2][1] = M[0][1]; M[2][2] = M[0][2]; for (unsigned int j = 0; j < 3; ++j) { returnValue[index + j] = W.at(i)[j][0] * M[j][0] + W.at(i)[j][1] * M[j][1] + W.at(i)[j][2] * M[j][2]; } } } void mitk::WeightedPointTransform::WeightedPointRegister(vtkPoints *X, vtkPoints *Y, const CovarianceMatrixList &Sigma_X, const CovarianceMatrixList &Sigma_Y, double Threshold, int MaxIterations, Rotation &TransformationR, Translation &TransformationT, double &FRE, int &n) { double FRE_identity = 0.0; double FRE_isotropic_weighted = 0.0; double initialFRE = 0.0; // set config_change to infinite (max double) at start double config_change = std::numeric_limits::max(); Rotation initial_TransformationR; initial_TransformationR.SetIdentity(); Translation initial_TransformationT; initial_TransformationT.Fill(0.0); // Weightmatrices Matrix3x3List W; vtkPoints *X_transformed = vtkPoints::New(); vtkPoints *X_transformedNew = vtkPoints::New(); vnl_vector oldq; itk::VariableSizeMatrix iA; vnl_vector iB; // initialize memory W.resize(X->GetNumberOfPoints()); X_transformed->SetNumberOfPoints(X->GetNumberOfPoints()); X_transformedNew->SetNumberOfPoints(X->GetNumberOfPoints()); iA.SetSize(3u * X->GetNumberOfPoints(), 6u); iB.set_size(3u * X->GetNumberOfPoints()); // calculate FRE_0 with identity transform FRE_identity = ComputeWeightedFRE( X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, initial_TransformationR, initial_TransformationT); MITK_DEBUG << "FRE for identity transform: " << FRE_identity; // compute isotropic transformation as initial estimate IsotropicRegistration(X, Y, m_LandmarkTransform, initial_TransformationR, initial_TransformationT); // result of unweighted registration algorithm TransformationR = initial_TransformationR; TransformationT = initial_TransformationT; // calculate FRE_0 with isotropic transform FRE_isotropic_weighted = ComputeWeightedFRE(X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, TransformationR, TransformationT); MITK_DEBUG << "FRE for transform obtained with unweighted registration: " << FRE_isotropic_weighted; // if R,t is worse than the identity, use the identity as initial transform if (FRE_isotropic_weighted < FRE_identity) { initialFRE = FRE_isotropic_weighted; } else { initialFRE = FRE_identity; TransformationR.SetIdentity(); // set rotation to identity element TransformationT.Fill(0.0); // set translation to identity element initial_TransformationR.SetIdentity(); initial_TransformationT.Fill(0.0); } // apply transform to moving set: mitk::AnisotropicRegistrationCommon::TransformPoints(X, X_transformed, TransformationR, TransformationT); // start with iteration 0 n = 0; do { n++; calculateWeightMatrices(Sigma_X, Sigma_Y, W, TransformationR); //''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' // PROBLEM: no square matrix but the backslash operator in matlab does solve the system anyway. How to convert this // to C++? // good descriptons to the "backslash"-operator (in german): // http://www.tm-mathe.de/Themen/html/matlab__zauberstab__backslash-.html // http://www.tm-mathe.de/Themen/html/matlab__matrix-division__vorsi.html#HoheMatrixA // // current method: treat the problem as a minimization problem, because this is what the // "backslash"-operator also does with "high" matrices. // (and we will have those matrices in most cases) C_maker(X_transformed, W, iA); E_maker(X_transformed, Y, W, iB); vnl_matrix_inverse myInverse(iA.GetVnlMatrix()); vnl_vector q = myInverse.pinverse(iB.size()) * iB; //''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' if (n > 1) q = (q + oldq) / 2; oldq = q; itk::Vector delta_t; delta_t[0] = q[3]; delta_t[1] = q[4]; delta_t[2] = q[5]; Matrix3x3 delta_theta; delta_theta[0][0] = 1; delta_theta[0][1] = -q[2]; delta_theta[0][2] = q[1]; delta_theta[1][0] = q[2]; delta_theta[1][1] = 1; delta_theta[1][2] = -q[0]; delta_theta[2][0] = -q[1]; delta_theta[2][1] = q[0]; delta_theta[2][2] = 1; - vnl_svd svd_delta_theta(delta_theta.GetVnlMatrix()); + vnl_svd svd_delta_theta(delta_theta.GetVnlMatrix().as_ref()); // convert vnl matrices to itk matrices... Matrix3x3 U; Matrix3x3 V; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { U[i][j] = svd_delta_theta.U()[i][j]; V[i][j] = svd_delta_theta.V()[i][j]; } } Matrix3x3 delta_R = U * V.GetTranspose(); // update rotation TransformationR = delta_R * TransformationR; // update translation TransformationT = delta_R * TransformationT + delta_t; // update moving points mitk::AnisotropicRegistrationCommon::TransformPoints(X, X_transformedNew, TransformationR, TransformationT); // calculate config change config_change = CalculateConfigChange(X_transformed, X_transformedNew); // swap the pointers the old set for the next iteration is // the new set of the last iteration vtkPoints *tmp = X_transformed; X_transformed = X_transformedNew; X_transformedNew = tmp; } while (config_change > Threshold && n < MaxIterations); // calculate FRE with current transform FRE = ComputeWeightedFRE(X, Y, Sigma_X, Sigma_Y, m_FRENormalizationFactor, W, TransformationR, TransformationT); MITK_DEBUG << "FRE after algorithm (prior to check with initial): " << FRE; // compare with FRE_initial if (initialFRE < FRE) { MITK_WARN << "FRE did not improve in anisotropic point registration function"; TransformationR = initial_TransformationR; TransformationT = initial_TransformationT; FRE = initialFRE; } MITK_DEBUG << "FRE final: " << FRE; X_transformed->Delete(); X_transformedNew->Delete(); } void mitk::WeightedPointTransform::SetMovingPointSet(vtkSmartPointer p) { m_MovingPointSet = p; } void mitk::WeightedPointTransform::SetCovarianceMatricesMoving(const CovarianceMatrixList &matrices) { m_CovarianceMatricesMoving = matrices; } void mitk::WeightedPointTransform::SetFixedPointSet(vtkSmartPointer p) { m_FixedPointSet = p; } void mitk::WeightedPointTransform::SetCovarianceMatricesFixed(const CovarianceMatrixList &matrices) { m_CovarianceMatricesFixed = matrices; } double mitk::WeightedPointTransform::CalculateConfigChange(vtkPoints *X, vtkPoints *X_new) { double sum[3] = {0.0, 0.0, 0.0}; double mean[3] = {0.0, 0.0, 0.0}; double pX[3] = {0.0, 0.0, 0.0}; double pX_new[3] = {0.0, 0.0, 0.0}; // compute mean of the old point set and the first sum for (vtkIdType i = 0; i < X->GetNumberOfPoints(); ++i) { X->GetPoint(i, pX); X_new->GetPoint(i, pX_new); // first sum sum[0] += (pX_new[0] - pX[0]) * (pX_new[0] - pX[0]); sum[1] += (pX_new[1] - pX[1]) * (pX_new[1] - pX[1]); sum[2] += (pX_new[2] - pX[2]) * (pX_new[2] - pX[2]); // mean mean[0] += pX[0]; mean[1] += pX[1]; mean[2] += pX[2]; } mean[0] /= X->GetNumberOfPoints(); mean[1] /= X->GetNumberOfPoints(); mean[2] /= X->GetNumberOfPoints(); const double us = sum[0] + sum[1] + sum[2]; // reset sum sum[0] = sum[1] = sum[2] = 0.0; for (vtkIdType i = 0; i < X->GetNumberOfPoints(); ++i) { X->GetPoint(i, pX); sum[0] += (pX[0] - mean[0]) * (pX[0] - mean[0]); sum[1] += (pX[1] - mean[1]) * (pX[1] - mean[1]); sum[2] += (pX[2] - mean[2]) * (pX[2] - mean[2]); } const double ls = sum[0] + sum[1] + sum[2]; return sqrt(us / ls); } diff --git a/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp b/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp index 87269991c4..29dfb3d4d7 100644 --- a/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp +++ b/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp @@ -1,500 +1,500 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTransformationOperation.h" #include #include #include #include #include #include // Wavelet #include #include #include #include #include #include #include #include -#include +#include #include "itkZeroFluxNeumannBoundaryCondition.h" #include "itkPeriodicBoundaryCondition.h" #include "itkConstantBoundaryCondition.h" //#include #include "itkCastImageFilter.h" #include "itkUnaryFunctorImageFilter.h" #include #include #include namespace mitk { namespace Functor { template< class TInput> class ThresholdValue { public: ThresholdValue() {}; ~ThresholdValue() {}; bool operator!=(const ThresholdValue &) const { return false; } bool operator==(const ThresholdValue & other) const { return !(*this != other); } inline unsigned short operator()(const TInput & A) const { if (A < value) return 0; else return 1; } double value = 0.0; }; template< class TInput, class TOutput> class RoundValue { public: RoundValue() {}; ~RoundValue() {}; bool operator!=(const RoundValue &) const { return false; } bool operator==(const RoundValue & other) const { return !(*this != other); } inline TOutput operator()(const TInput & A) const { return std::round(A); } }; } } template static void ExecuteMultiResolution(itk::Image* image, unsigned int numberOfLevels, bool outputAsDouble, std::vector &resultImages) { typedef itk::Image ImageType; typedef itk::Image DoubleOutputType; typedef itk::RecursiveMultiResolutionPyramidImageFilter ImageTypeFilterType; typedef itk::RecursiveMultiResolutionPyramidImageFilter DoubleTypeFilterType; if (outputAsDouble) { typename DoubleTypeFilterType::Pointer recursiveMultiResolutionPyramidImageFilter = DoubleTypeFilterType::New(); recursiveMultiResolutionPyramidImageFilter->SetInput(image); recursiveMultiResolutionPyramidImageFilter->SetNumberOfLevels(numberOfLevels); recursiveMultiResolutionPyramidImageFilter->Update(); // This outputs the levels (0 is the lowest resolution) for (unsigned int i = 0; i < numberOfLevels; ++i) { mitk::Image::Pointer outputImage = mitk::Image::New(); CastToMitkImage(recursiveMultiResolutionPyramidImageFilter->GetOutput(i), outputImage); resultImages.push_back(outputImage); } } else { typename ImageTypeFilterType::Pointer recursiveMultiResolutionPyramidImageFilter = ImageTypeFilterType::New(); recursiveMultiResolutionPyramidImageFilter->SetInput(image); recursiveMultiResolutionPyramidImageFilter->SetNumberOfLevels(numberOfLevels); recursiveMultiResolutionPyramidImageFilter->Update(); // This outputs the levels (0 is the lowest resolution) for (unsigned int i = 0; i < numberOfLevels; ++i) { mitk::Image::Pointer outputImage = mitk::Image::New(); CastToMitkImage(recursiveMultiResolutionPyramidImageFilter->GetOutput(i), outputImage); resultImages.push_back(outputImage); } } } std::vector mitk::TransformationOperation::MultiResolution(Image::Pointer & image, unsigned int numberOfLevels, bool outputAsDouble) { std::vector resultImages; AccessByItk_n(image, ExecuteMultiResolution, (numberOfLevels, outputAsDouble, resultImages)); return resultImages; } template static void ExecuteLaplacianOfGaussian(itk::Image* image, double sigma, bool outputAsDouble, mitk::Image::Pointer &resultImage) { typedef itk::Image ImageType; typedef itk::Image DoubleOutputType; typedef itk::LaplacianRecursiveGaussianImageFilter ImageTypeFilterType; typedef itk::LaplacianRecursiveGaussianImageFilter DoubleTypeFilterType; if (outputAsDouble) { typename DoubleTypeFilterType::Pointer filter = DoubleTypeFilterType::New(); filter->SetInput(image); filter->SetSigma(sigma); filter->Update(); CastToMitkImage(filter->GetOutput(), resultImage); } else { typename ImageTypeFilterType::Pointer filter = ImageTypeFilterType::New(); filter->SetInput(image); filter->SetSigma(sigma); filter->Update(); CastToMitkImage(filter->GetOutput(), resultImage); } } mitk::Image::Pointer mitk::TransformationOperation::LaplacianOfGaussian(Image::Pointer & image, double sigma, bool outputAsDouble) { Image::Pointer resultImage; AccessByItk_n(image, ExecuteLaplacianOfGaussian, (sigma, outputAsDouble, resultImage)); return resultImage; } template static void ExecuteSpecificWaveletTransformation(itk::Image* image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, std::vector &resultImages) { const unsigned int Dimension = VImageDimension; typedef TInputPixel PixelType; typedef TOutputPixel OutputPixelType; typedef itk::Image< PixelType, Dimension > ImageType; typedef itk::Image< double, Dimension > DoubleImageType; typedef itk::Image< OutputPixelType, Dimension > OutputImageType; typedef itk::CastImageFilter< ImageType, DoubleImageType > CastFilterType; - typedef itk::FFTPadPositiveIndexImageFilter< DoubleImageType > FFTPadType; + typedef itk::FFTPadImageFilter< DoubleImageType > FFTPadType; typedef itk::ForwardFFTImageFilter< DoubleImageType, itk::Image< std::complex, Dimension> > FFTFilterType; typedef typename FFTFilterType::OutputImageType ComplexImageType; typedef TWaveletFunction WaveletFunctionType; typedef itk::WaveletFrequencyFilterBankGenerator< ComplexImageType, WaveletFunctionType > WaveletFilterBankType; typedef itk::WaveletFrequencyForward< ComplexImageType, ComplexImageType, WaveletFilterBankType > ForwardWaveletType; typedef itk::InverseFFTImageFilter< ComplexImageType, OutputImageType > InverseFFTFilterType; // Convert input parameter unsigned int highSubBands = numberOfBands; //inputBands; unsigned int levels = numberOfLevels; // Perform FFT on input image typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput(image); // Pad Image so it fits the expect typename FFTPadType::Pointer fftpad = FFTPadType::New(); fftpad->SetSizeGreatestPrimeFactor(4); itk::ConstantBoundaryCondition< DoubleImageType > constantBoundaryCondition; itk::PeriodicBoundaryCondition< DoubleImageType > periodicBoundaryCondition; itk::ZeroFluxNeumannBoundaryCondition< DoubleImageType > zeroFluxNeumannBoundaryCondition; switch (condition) { case mitk::BorderCondition::Constant: fftpad->SetBoundaryCondition(&constantBoundaryCondition); break; case mitk::BorderCondition::Periodic: fftpad->SetBoundaryCondition(&periodicBoundaryCondition); break; case mitk::BorderCondition::ZeroFluxNeumann: fftpad->SetBoundaryCondition(&zeroFluxNeumannBoundaryCondition); break; default: break; } fftpad->SetInput(castFilter->GetOutput()); typename FFTFilterType::Pointer fftFilter = FFTFilterType::New(); fftFilter->SetInput(fftpad->GetOutput()); // Calculate forward transformation typename ForwardWaveletType::Pointer forwardWavelet = ForwardWaveletType::New(); forwardWavelet->SetHighPassSubBands(highSubBands); forwardWavelet->SetLevels(levels); forwardWavelet->SetInput(fftFilter->GetOutput()); forwardWavelet->Update(); // Obtain target spacing, size and origin typename ComplexImageType::SpacingType inputSpacing; for (unsigned int i = 0; i < Dimension; ++i) { inputSpacing[i] = image->GetLargestPossibleRegion().GetSize()[i]; } typename ComplexImageType::SpacingType expectedSpacing = inputSpacing; typename ComplexImageType::PointType inputOrigin = image->GetOrigin(); typename ComplexImageType::PointType expectedOrigin = inputOrigin; typename ComplexImageType::SizeType inputSize = fftFilter->GetOutput()->GetLargestPossibleRegion().GetSize(); typename ComplexImageType::SizeType expectedSize = inputSize; // Inverse FFT to obtain filtered images typename InverseFFTFilterType::Pointer inverseFFT = InverseFFTFilterType::New(); for (unsigned int level = 0; level < numberOfLevels + 1; ++level) { double scaleFactorPerLevel = std::pow(static_cast< double >(forwardWavelet->GetScaleFactor()),static_cast< double >(level)); for (unsigned int i = 0; i < Dimension; ++i) { expectedSize[i] = inputSize[i] / scaleFactorPerLevel; expectedOrigin[i] = inputOrigin[i]; expectedSpacing[i] = inputSpacing[i] * scaleFactorPerLevel; } for (unsigned int band = 0; band < highSubBands; ++band) { unsigned int nOutput = level * forwardWavelet->GetHighPassSubBands() + band; // Do not compute bands in low-pass level. if (level == numberOfLevels && band == 0) { nOutput = forwardWavelet->GetTotalOutputs() - 1; } else if (level == numberOfLevels && band != 0) { break; } inverseFFT->SetInput(forwardWavelet->GetOutput(nOutput)); inverseFFT->Update(); auto itkOutputImage = inverseFFT->GetOutput(); itkOutputImage->SetSpacing(expectedSpacing); mitk::Image::Pointer outputImage = mitk::Image::New(); CastToMitkImage(itkOutputImage, outputImage); resultImages.push_back(outputImage); } } } template static void ExecuteWaveletTransformation(itk::Image* image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, mitk::WaveletType waveletType, std::vector &resultImages) { typedef itk::Point< double, VImageDimension > PointType; typedef itk::HeldIsotropicWavelet< double, VImageDimension, PointType > HeldIsotropicWaveletType; typedef itk::VowIsotropicWavelet< double, VImageDimension, PointType > VowIsotropicWaveletType; typedef itk::SimoncelliIsotropicWavelet< double, VImageDimension, PointType > SimoncelliIsotropicWaveletType; typedef itk::ShannonIsotropicWavelet< double, VImageDimension, PointType > ShannonIsotropicWaveletType; switch (waveletType) { case mitk::WaveletType::Held: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; case mitk::WaveletType::Shannon: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; case mitk::WaveletType::Simoncelli: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; case mitk::WaveletType::Vow: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; default: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; } } std::vector mitk::TransformationOperation::WaveletForward(Image::Pointer & image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, mitk::WaveletType waveletType) { std::vector resultImages; AccessByItk_n(image, ExecuteWaveletTransformation, (numberOfLevels, numberOfBands, condition, waveletType, resultImages)); return resultImages; } template static void ExecuteImageTypeToDouble(itk::Image* image, mitk::Image::Pointer &outputImage) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< double, VImageDimension > DoubleImageType; typedef itk::CastImageFilter< ImageType, DoubleImageType > CastFilterType; typedef itk::ImageDuplicator< DoubleImageType > DuplicatorType; // Perform FFT on input image typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput(image); castFilter->Update(); typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(castFilter->GetOutput()); duplicator->Update(); CastToMitkImage(duplicator->GetOutput(), outputImage); } template static void ExecuteRoundImage(itk::Image* /*image*/, mitk::Image::Pointer resampledImage, mitk::Image::Pointer &outputImage) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< double, VImageDimension > DoubleImageType; typedef itk::UnaryFunctorImageFilter< DoubleImageType, ImageType, mitk::Functor::RoundValue > DefaultFilterType; typename DoubleImageType::Pointer itkImage = DoubleImageType::New(); mitk::CastToItkImage(resampledImage, itkImage); typename DefaultFilterType::Pointer filter = DefaultFilterType::New(); filter->SetInput(itkImage); filter->Update(); CastToMitkImage(filter->GetOutput(), outputImage); } mitk::Image::Pointer mitk::TransformationOperation::ResampleImage(Image::Pointer &image, mitk::Vector3D spacingVector, mitk::ImageMappingInterpolator::Type interpolator, GridInterpolationPositionType position, bool returnAsDouble, bool roundOutput) { // Convert image to double if required mitk::Image::Pointer tmpImage = image; if (returnAsDouble) { AccessByItk_n(image, ExecuteImageTypeToDouble, (tmpImage)); } auto newGeometry = image->GetGeometry()->Clone(); mitk::Vector3D spacing; mitk::BaseGeometry::BoundsArrayType bounds = newGeometry->GetBounds(); for (int i = 0; i < 3; ++i) { spacing[i] = newGeometry->GetSpacing()[i]; //bounds[i*2+1] = newGeometry->GetBounds()[i * 2 + 1]; if (spacingVector[i] > 0) { spacing[i] = spacingVector[i]; if (position == mitk::GridInterpolationPositionType::SameSize) { unsigned int samples = image->GetDimensions()[i]; double currentSpacing = newGeometry->GetSpacing()[i]; double newFactor = std::floor(samples*currentSpacing / spacingVector[i]); spacing[i] = samples * currentSpacing / newFactor; } } bounds[i * 2] = 0; bounds[i*2+1] = std::ceil(bounds[i*2+1] * newGeometry->GetSpacing()[i] *1.0 / spacing[i]); } mitk::Point3D origin = newGeometry->GetOrigin(); if (position == mitk::GridInterpolationPositionType::CenterAligned) { for (int i = 0; i < 3; ++i) { double oldLength = newGeometry->GetSpacing()[i] * newGeometry->GetBounds()[i*2+1]; double newLength = spacing[i] * bounds[i*2+1]; origin[i] = origin[i] - (newLength - oldLength) / 2; } } newGeometry->SetSpacing(spacing); newGeometry->SetOrigin(origin); newGeometry->SetBounds(bounds); mitk::Image::Pointer tmpResult = ImageMappingHelper::map( tmpImage, mitk::GenerateIdentityRegistration3D().GetPointer(), false, 0.0, //Padding Value newGeometry.GetPointer(), false, 0, //Error Value interpolator ); mitk::Image::Pointer result = mitk::Image::New(); if (roundOutput) { AccessByItk_n(tmpImage, ExecuteRoundImage, (tmpResult, result)); } else { result = tmpResult; } return result; } template static void ExecuteImageThresholding(itk::Image* image, mitk::Image::Pointer &resultImage) { typedef itk::Image ImageType; typedef itk::Image MaskType; typedef itk::UnaryFunctorImageFilter< ImageType, MaskType, mitk::Functor::ThresholdValue > DefaultFilterType; typename DefaultFilterType::Pointer filter = DefaultFilterType::New(); filter->SetInput(image); filter->GetFunctor().value = 0.5; filter->Update(); CastToMitkImage(filter->GetOutput(), resultImage); } mitk::Image::Pointer mitk::TransformationOperation::ResampleMask(Image::Pointer &image, mitk::Vector3D spacingVector, mitk::ImageMappingInterpolator::Type interpolator, GridInterpolationPositionType position) { mitk::Image::Pointer result; if (interpolator == mitk::ImageMappingInterpolator::NearestNeighbor) { result = TransformationOperation::ResampleImage(image, spacingVector, interpolator, position, false, false); } else { auto tmpResult = TransformationOperation::ResampleImage(image, spacingVector, interpolator, position, true, false); AccessByItk_n(tmpResult, ExecuteImageThresholding, (result)); } return result; } namespace itk { namespace utils { IndexPairType IndexToLevelBandSteerablePyramid(unsigned int linearIndex, unsigned int levels, unsigned int bands) { unsigned int totalOutputs = 1 + levels * bands; if (linearIndex > totalOutputs - 1) { itkGenericExceptionMacro(<< "Failed converting linearIndex " << linearIndex << " with levels: " << levels << " bands: " << bands << " to Level,Band pair : out of bounds"); } // Low pass (band = 0). if (linearIndex == totalOutputs - 1) { return std::make_pair(levels - 1, 0); } unsigned int band = (linearIndex) % bands + 1; // note integer division ahead. unsigned int level = (linearIndex) / bands; itkAssertInDebugAndIgnoreInReleaseMacro(level < levels); return std::make_pair(level, band); } // Instantiation template unsigned int ComputeMaxNumberOfLevels<3>(const Size< 3 >& inputSize, const unsigned int & scaleFactor); template unsigned int ComputeMaxNumberOfLevels<2>(const Size< 2 >& inputSize, const unsigned int & scaleFactor); } // end namespace utils } // end namespace itk diff --git a/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h b/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h index bedca93e42..91cc4c2c38 100644 --- a/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h +++ b/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h @@ -1,136 +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 mitkBoundingShapeInteractor_h #define mitkBoundingShapeInteractor_h #include #include #include #include #include namespace mitk { // create events for interactions #pragma GCC visibility push(default) - itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent); + itkEventMacroDeclaration(BoundingShapeInteractionEvent, itk::AnyEvent); #pragma GCC visibility pop /** * @brief Basic interaction methods for mitk::GeometryData * * Inherit from DataInteratcor, this provides functionality of a state machine and configurable inputs. * * \ingroup Interaction */ class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor : public DataInteractor { public: mitkClassMacro(BoundingShapeInteractor, DataInteractor); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void SetDataNode(DataNode *dataNode) override; void SetRotationEnabled(bool rotationEnabled); protected: BoundingShapeInteractor(); ~BoundingShapeInteractor() override; /** * Here actions strings from the loaded state machine pattern are mapped to functions of * the DataInteractor. These functions are called when an action from the state machine pattern is executed. */ void ConnectActionsAndFunctions() override; /** * @brief Called when a DataNode has been set/changed. */ void DataNodeChanged() override; void HandlePositionChanged(const InteractionEvent *interactionEvent, Point3D ¢er); /** * @brief Checks if the mouse pointer is over the object. */ virtual bool CheckOverObject(const InteractionEvent *); /** * @brief Checks if the mouse pointer is over one of the assigned handles. */ virtual bool CheckOverHandles(const InteractionEvent *interactionEvent); /** * @brief Called if the mouse pointer is over the object indicated by a color change */ virtual void SelectObject(StateMachineAction *, InteractionEvent *); /** * @brief Called if the mouse pointer leaves the area of the object */ virtual void DeselectObject(StateMachineAction *, InteractionEvent *); /** * @brief Called if the mouse pointer is over one of the handles indicated by a color change */ virtual void SelectHandle(StateMachineAction *, InteractionEvent *); /** * @brief Performs a translation of the object relative to the mouse movement */ virtual void TranslateObject(StateMachineAction *, InteractionEvent *); /** * @brief Performs a object shape change by influencing the scaling of the initial bounding box */ virtual void ScaleObject(StateMachineAction *, InteractionEvent *); /** * @brief Initializes the movement, stores starting position */ virtual void InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent); /** * @brief Deselects all Handles at the end of interaction */ virtual void DeselectHandles(StateMachineAction *, InteractionEvent *interactionEvent); /** * @brief Restore default properties of bounding box and handles */ virtual void RestoreNodeProperties(); /** * @brief Initializes member variables. */ bool InitMembers(InteractionEvent *interactionEvent); private: /** * @brief Enables default crosshair properties */ void EnableCrosshairNavigation(); /** * @brief Sets limited crosshair properties (disable crosshair movement) */ void DisableCrosshairNavigation(); class Impl; Impl *m_Impl; }; } #endif diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp index ce24457391..4125f76aa8 100644 --- a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp +++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp @@ -1,626 +1,628 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "../DataManagement/mitkBoundingShapeUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "usGetModuleContext.h" #include "usModuleRegistry.h" // Properties to allow the user to interact with the base data const char *selectedColorPropertyName = "Bounding Shape.Selected Color"; const char *deselectedColorPropertyName = "Bounding Shape.Deselected Color"; const char *activeHandleIdPropertyName = "Bounding Shape.Active Handle ID"; const char *boundingShapePropertyName = "Bounding Shape"; namespace mitk { + itkEventMacroDefinition(BoundingShapeInteractionEvent, itk::AnyEvent); + class BoundingShapeInteractor::Impl { public: Impl() : ScrollEnabled(false), RotationEnabled(false) { Point3D initialPoint; initialPoint.Fill(0.0); for (int i = 0; i < 6; ++i) Handles.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } ~Impl() {} bool ScrollEnabled; Point3D InitialPickedWorldPoint; Point3D LastPickedWorldPoint; Point2D InitialPickedDisplayPoint; std::vector Handles; Handle ActiveHandle; Geometry3D::Pointer OriginalGeometry; bool RotationEnabled; std::map DisplayInteractorConfigs; }; } mitk::BoundingShapeInteractor::BoundingShapeInteractor() : m_Impl(new Impl) { } mitk::BoundingShapeInteractor::~BoundingShapeInteractor() { this->RestoreNodeProperties(); delete m_Impl; } void mitk::BoundingShapeInteractor::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually // executing an action CONNECT_CONDITION("isHoveringOverObject", CheckOverObject); CONNECT_CONDITION("isHoveringOverHandles", CheckOverHandles); // **Function** in the statemachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject", SelectObject); CONNECT_FUNCTION("deselectObject", DeselectObject); CONNECT_FUNCTION("deselectHandles", DeselectHandles); CONNECT_FUNCTION("initInteraction", InitInteraction); CONNECT_FUNCTION("translateObject", TranslateObject); CONNECT_FUNCTION("selectHandle", SelectHandle); CONNECT_FUNCTION("scaleObject", ScaleObject); // CONNECT_FUNCTION("rotateObject",RotateObject); } // RotateObject(StateMachineAction*, InteractionEvent* interactionEvent) // void mitk::BoundingShapeInteractor::RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry* // geometry) //{ // mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis); // float pointX = 0.0f; // float pointY = 0.0f; // float pointZ = 0.0f; // mitk::Point3D pointOfRotation; // pointOfRotation.Fill(0.0); // this->GetDataNode()->GetFloatProperty(anchorPointX, pointX); // this->GetDataNode()->GetFloatProperty(anchorPointY, pointY); // this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ); // pointOfRotation[0] = pointX; // pointOfRotation[1] = pointY; // pointOfRotation[2] = pointZ; // // mitk::RotationOperation* doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle); // // geometry->ExecuteOperation(doOp); // delete doOp; //} void mitk::BoundingShapeInteractor::SetRotationEnabled(bool rotationEnabled) { m_Impl->RotationEnabled = rotationEnabled; } void mitk::BoundingShapeInteractor::DataNodeChanged() { mitk::DataNode::Pointer newInputNode = this->GetDataNode(); if (newInputNode == nullptr) return; // add color properties mitk::ColorProperty::Pointer selectedColor = dynamic_cast(newInputNode->GetProperty(selectedColorPropertyName)); mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(newInputNode->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNull()) newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0)); if (deselectedColor.IsNull()) newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 1.0, 1.0)); newInputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(true)); newInputNode->AddProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); newInputNode->SetProperty("layer", mitk::IntProperty::New(101)); newInputNode->SetBoolProperty("fixedLayer", mitk::BoolProperty::New(true)); newInputNode->SetBoolProperty("pickable", true); mitk::ColorProperty::Pointer initialColor = dynamic_cast(newInputNode->GetProperty(deselectedColorPropertyName)); if (initialColor.IsNotNull()) { newInputNode->SetColor(initialColor->GetColor()); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::HandlePositionChanged(const InteractionEvent *interactionEvent, Point3D ¢er) { GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); std::vector cornerPoints = GetCornerPoints(geometry, true); if (m_Impl->Handles.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->Handles[0].SetPosition(pointLeft); m_Impl->Handles[1].SetPosition(pointRight); m_Impl->Handles[2].SetPosition(pointTop); m_Impl->Handles[3].SetPosition(pointBottom); m_Impl->Handles[4].SetPosition(pointFront); m_Impl->Handles[5].SetPosition(pointBack); // calculate center based on half way of the distance between two opposing cornerpoints center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); } } void mitk::BoundingShapeInteractor::SetDataNode(DataNode *node) { this->RestoreNodeProperties(); // if there is another node set, restore it's color if (node == nullptr) return; DataInteractor::SetDataNode(node); // calls DataNodeChanged internally this->DataNodeChanged(); } bool mitk::BoundingShapeInteractor::CheckOverObject(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); // calculates translation based on offset+extent not on the transformation matrix (because the cube is located in the // center not in the origin) vtkSmartPointer imageTransform = geometry->GetVtkTransform()->GetMatrix(); Point3D center = geometry->GetCenter(); auto translation = vtkSmartPointer::New(); auto transform = vtkSmartPointer::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); mitk::Vector3D extent; for (unsigned int i = 0; i < 3; ++i) extent[i] = (geometry->GetExtent(i)); Point3D currentWorldPosition; Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); interactionEvent->GetSender()->DisplayToWorld(currentDisplayPosition, currentWorldPosition); ScalarType transformedPosition[4]; transformedPosition[0] = currentWorldPosition[0]; transformedPosition[1] = currentWorldPosition[1]; transformedPosition[2] = currentWorldPosition[2]; transformedPosition[3] = 1; // transform point from world to object coordinates transform->GetInverse()->TransformPoint(transformedPosition, transformedPosition); // check if the world point is within bounds bool isInside = (transformedPosition[0] >= (-extent[0] / 2.0)) && (transformedPosition[0] <= (extent[0] / 2.0)) && (transformedPosition[1] >= (-extent[1] / 2.0)) && (transformedPosition[1] <= (extent[1] / 2.0)) && (transformedPosition[2] >= (-extent[2] / 2.0)) && (transformedPosition[2] <= (extent[2] / 2.0)); return isInside; } bool mitk::BoundingShapeInteractor::CheckOverHandles(const InteractionEvent *interactionEvent) { Point3D boundingBoxCenter; HandlePositionChanged(interactionEvent, boundingBoxCenter); const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; Point2D displayCenterPoint; // to do: change to actual time step (currently not necessary because geometry remains the same for each timestep int timeStep = 0; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); std::vector cornerPoints = GetCornerPoints(geometry, true); interactionEvent->GetSender()->WorldToDisplay(boundingBoxCenter, displayCenterPoint); double scale = interactionEvent->GetSender()->GetScaleFactorMMPerDisplayUnit(); // GetDisplaySizeInMM mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else initialHandleSize = 1.0 / 40.0; mitk::Point2D displaysize = interactionEvent->GetSender()->GetDisplaySizeInMM(); ScalarType handlesize = ((displaysize[0] + displaysize[1]) / 2.0) * initialHandleSize; unsigned int handleNum = 0; for (auto &handle : m_Impl->Handles) { Point2D centerpoint; interactionEvent->GetSender()->WorldToDisplay(handle.GetPosition(), centerpoint); Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); if ((currentDisplayPosition.EuclideanDistanceTo(centerpoint) < (handlesize / scale)) && (currentDisplayPosition.EuclideanDistanceTo(displayCenterPoint) > (handlesize / scale))) // check if mouse is hovering over center point { handle.SetActive(true); m_Impl->ActiveHandle = handle; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(handleNum++)); this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return true; } else { handleNum++; handle.SetActive(false); } this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); } return false; } void mitk::BoundingShapeInteractor::SelectHandle(StateMachineAction *, InteractionEvent *) { this->DisableCrosshairNavigation(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNotNull()) { this->GetDataNode()->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectHandles(StateMachineAction *, InteractionEvent *) { this->DisableCrosshairNavigation(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::SelectObject(StateMachineAction *, InteractionEvent *) { this->DisableCrosshairNavigation(); // disable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(selectedColorPropertyName)); if (selectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectObject(StateMachineAction *, InteractionEvent *) { this->EnableCrosshairNavigation(); // enable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); if (deselectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", deselectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent) { InitMembers(interactionEvent); } bool mitk::BoundingShapeInteractor::InitMembers(InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; // get initial position coordinates m_Impl->InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); m_Impl->InitialPickedWorldPoint = positionEvent->GetPositionInWorld(); m_Impl->LastPickedWorldPoint = positionEvent->GetPositionInWorld(); return true; } void mitk::BoundingShapeInteractor::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); Vector3D spacing = geometry->GetSpacing(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); Vector3D interactionMove; // pixel aligned shifting of the bounding box interactionMove[0] = std::round((currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]) / spacing[0]) * spacing[0]; interactionMove[1] = std::round((currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]) / spacing[1]) * spacing[1]; interactionMove[2] = std::round((currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]) / spacing[2]) * spacing[2]; if ((interactionMove[0] + interactionMove[1] + interactionMove[2]) != 0.0) // only update current position if a movement occured { m_Impl->LastPickedWorldPoint = currentPickedPoint; geometry->SetOrigin(geometry->GetOrigin() + interactionMove); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } return; } void mitk::BoundingShapeInteractor::ScaleObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); Point3D handlePickedPoint = m_Impl->ActiveHandle.GetPosition(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); Vector3D spacing = geometry->GetSpacing(); // pixel aligned bounding box Vector3D interactionMove; interactionMove[0] = (currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]); interactionMove[1] = (currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]); interactionMove[2] = (currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]); std::vector faces = m_Impl->ActiveHandle.GetFaceIndices(); auto pointscontainer = mitk::BoundingBox::PointsContainer::New(); // calculate cornerpoints from geometry plus visualization offset std::vector cornerPoints = GetCornerPoints(geometry, true); unsigned int num = 0; for (const auto &point : cornerPoints) { pointscontainer->InsertElement(num++, point); } // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); Vector3D faceNormal; faceNormal[0] = handlePickedPoint[0] - center[0]; faceNormal[1] = handlePickedPoint[1] - center[1]; faceNormal[2] = handlePickedPoint[2] - center[2]; Vector3D faceShift = ((faceNormal * interactionMove) / (faceNormal.GetNorm() * faceNormal.GetNorm())) * faceNormal; // calculate cornerpoints from geometry without visualization offset to update actual geometry cornerPoints = GetCornerPoints(geometry, false); num = 0; for (const auto &point : cornerPoints) { pointscontainer->InsertElement(num++, point); } bool positionChangeThreshold = true; for (int numFaces = 0; numFaces < 8; numFaces++) // estimate the corresponding face and shift its assigned points { if ((numFaces != faces[0]) && (numFaces != faces[1]) && (numFaces != faces[2]) && (numFaces != faces[3])) { Point3D point = pointscontainer->GetElement(numFaces); if (m_Impl->RotationEnabled) // apply if geometry is rotated at a pixel aligned shift is not possible { point[0] += faceShift[0]; point[1] += faceShift[1]; point[2] += faceShift[2]; } else // shift pixelwise { point[0] += std::round(faceShift[0] / spacing[0]) * spacing[0]; point[1] += std::round(faceShift[1] / spacing[1]) * spacing[1]; point[2] += std::round(faceShift[2] / spacing[2]) * spacing[2]; } if (point == pointscontainer->GetElement(numFaces)) positionChangeThreshold = false; else m_Impl->LastPickedWorldPoint = point; pointscontainer->InsertElement(numFaces, point); } } if (positionChangeThreshold) // update only if bounding box is shifted at least by one pixel { auto inverse = mitk::AffineTransform3D::New(); geometry->GetIndexToWorldTransform()->GetInverse(inverse); for (unsigned int pointid = 0; pointid < 8; pointid++) { pointscontainer->InsertElement(pointid, inverse->TransformPoint(pointscontainer->GetElement(pointid))); } auto bbox = mitk::BoundingBox::New(); bbox->SetPoints(pointscontainer); bbox->ComputeBoundingBox(); mitk::Point3D BBmin = bbox->GetMinimum(); mitk::Point3D BBmax = bbox->GetMaximum(); if (std::abs(BBmin[0] - BBmax[0]) > 0.01 && std::abs(BBmin[1] - BBmax[1]) > 0.01 && std::abs(BBmin[2] - BBmax[2]) > 0.01) // TODO: check if the extent is greater than zero { geometry->SetBounds(bbox->GetBounds()); geometry->Modified(); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } return; } void mitk::BoundingShapeInteractor::RestoreNodeProperties() { mitk::DataNode::Pointer inputNode = this->GetDataNode(); if (inputNode.IsNull()) return; mitk::ColorProperty::Pointer color = (mitk::ColorProperty::New(1.0, 1.0, 1.0)); if (color.IsNotNull()) { inputNode->GetPropertyList()->SetProperty("color", color); } inputNode->SetProperty("layer", mitk::IntProperty::New(99)); inputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(false)); inputNode->GetPropertyList()->DeleteProperty(activeHandleIdPropertyName); EnableCrosshairNavigation(); // update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::EnableCrosshairNavigation() { // enable the crosshair navigation // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework for (auto it = m_Impl->DisplayInteractorConfigs.begin(); it != m_Impl->DisplayInteractorConfigs.end(); ++it) { if (it->first) { mitk::DisplayInteractor *displayInteractor = static_cast( us::GetModuleContext()->GetService(it->first)); if (displayInteractor != nullptr) { // here the regular configuration is loaded again displayInteractor->SetEventConfig(it->second); } mitk::DisplayActionEventBroadcast *displayActionEventBroadcast = static_cast( us::GetModuleContext()->GetService(it->first)); if (displayActionEventBroadcast != nullptr) { // here the regular configuration is loaded again displayActionEventBroadcast->SetEventConfig(it->second); } } } m_Impl->DisplayInteractorConfigs.clear(); m_Impl->ScrollEnabled = true; } void mitk::BoundingShapeInteractor::DisableCrosshairNavigation() { // dont deactivate twice, else we will clutter the config list ... if (m_Impl->ScrollEnabled == false) return; // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts // with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction // will still be enabled m_Impl->DisplayInteractorConfigs.clear(); std::vector> listEventObserver = us::GetModuleContext()->GetServiceReferences(); for (auto it = listEventObserver.begin(); it != listEventObserver.end(); ++it) { auto *displayInteractor = dynamic_cast(us::GetModuleContext()->GetService(*it)); if (displayInteractor != nullptr) { // remember the original configuration m_Impl->DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig())); // here the alternative configuration is loaded displayInteractor->AddEventConfig("DisplayConfigBlockLMB.xml"); } auto *displayActionEventBroadcast = dynamic_cast(us::GetModuleContext()->GetService(*it)); if (displayActionEventBroadcast != nullptr) { // remember the original configuration m_Impl->DisplayInteractorConfigs.insert(std::make_pair(*it, displayActionEventBroadcast->GetEventConfig())); // here the alternative configuration is loaded displayActionEventBroadcast->AddEventConfig("DisplayConfigBlockLMB.xml"); } } m_Impl->ScrollEnabled = false; } diff --git a/Modules/CameraCalibration/mitkCameraIntrinsics.cpp b/Modules/CameraCalibration/mitkCameraIntrinsics.cpp index fbb1ce2894..2613586ad7 100644 --- a/Modules/CameraCalibration/mitkCameraIntrinsics.cpp +++ b/Modules/CameraCalibration/mitkCameraIntrinsics.cpp @@ -1,508 +1,506 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkCameraIntrinsics.h" -#include #include #include #include mitk::CameraIntrinsics::CameraIntrinsics() - : m_Valid(false), m_Mutex(itk::FastMutexLock::New()) + : m_Valid(false) { m_CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType::type); m_CameraMatrix.at(2,2) = 1.0; m_DistorsionCoeffs = cv::Mat::zeros(1, 5, cv::DataType::type); } mitk::CameraIntrinsics::CameraIntrinsics(const CameraIntrinsics& other) : itk::Object() , mitk::XMLSerializable() , m_Valid(false) - , m_Mutex(itk::FastMutexLock::New()) { this->Copy(&other); } mitk::CameraIntrinsics::~CameraIntrinsics() { } bool mitk::CameraIntrinsics::Equals( const CameraIntrinsics* other ) const { return other->GetDistorsionCoeffsAsPoint4D()== this->GetDistorsionCoeffsAsPoint4D() && other->GetFocalPoint()== this->GetFocalPoint() && other->GetPrincipalPoint() == this->GetPrincipalPoint(); } void mitk::CameraIntrinsics::Copy(const CameraIntrinsics* other) { this->SetIntrinsics( other->GetCameraMatrix().clone() , other->GetDistorsionCoeffs().clone() ); this->SetValid(other->m_Valid); } bool mitk::CameraIntrinsics::IsValid() const { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); return m_Valid; } vnl_matrix_fixed mitk::CameraIntrinsics::GetVnlCameraMatrix() const { vnl_matrix_fixed mat; mat.set_identity(); { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); mat(0,0) = m_CameraMatrix.at(0,0); mat(1,1) = m_CameraMatrix.at(1,1); mat(0,2) = m_CameraMatrix.at(0,2); mat(1,2) = m_CameraMatrix.at(1,2); } return mat; } void mitk::CameraIntrinsics::SetCameraMatrix( const vnl_matrix_fixed& _CameraMatrix ) { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); m_CameraMatrix.at(0,0) = _CameraMatrix(0,0); m_CameraMatrix.at(1,1) = _CameraMatrix(1,1); m_CameraMatrix.at(0,2) = _CameraMatrix(0,2); m_CameraMatrix.at(1,2) = _CameraMatrix(1,2); } vnl_matrix_fixed mitk::CameraIntrinsics::GetVnlCameraMatrix3x4() const { vnl_matrix_fixed mat; mat.fill(0); mat.update( this->GetVnlCameraMatrix().as_matrix() ); return mat; } void mitk::CameraIntrinsics::SetIntrinsics( const cv::Mat& _CameraMatrix , const cv::Mat& _DistorsionCoeffs) { { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); if( _CameraMatrix.cols != 3 || _CameraMatrix.rows != 3) throw std::invalid_argument("Wrong format of camera matrix. Should be 3x3" " double."); endoAssertMsg( (_DistorsionCoeffs.cols == 5) && _DistorsionCoeffs.rows == 1, "Wrong format of distorsion coefficients" " vector. Should be 5x1 double."); m_CameraMatrix = _CameraMatrix.clone(); m_DistorsionCoeffs = _DistorsionCoeffs.clone(); m_Valid = true; } this->Modified(); } void mitk::CameraIntrinsics::SetIntrinsics( const mitk::Point3D& focalPoint, const mitk::Point3D& principalPoint, const mitk::Point4D& distortionCoefficients) { { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); m_CameraMatrix.at(0,0) = focalPoint[0]; m_CameraMatrix.at(1,1) = focalPoint[1]; m_CameraMatrix.at(0,2) = principalPoint[0]; m_CameraMatrix.at(1,2) = principalPoint[1]; m_DistorsionCoeffs.at(0,0) = distortionCoefficients[0]; m_DistorsionCoeffs.at(0,1) = distortionCoefficients[1]; m_DistorsionCoeffs.at(0,2) = distortionCoefficients[2]; m_DistorsionCoeffs.at(0,3) = distortionCoefficients[3]; } this->Modified(); } void mitk::CameraIntrinsics::SetFocalLength( double x, double y ) { { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); m_CameraMatrix.at(0,0) = x; m_CameraMatrix.at(1,1) = y; } this->Modified(); } void mitk::CameraIntrinsics::SetPrincipalPoint( double x, double y ) { { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); m_CameraMatrix.at(0,2) = x; m_CameraMatrix.at(1,2) = y; } this->Modified(); } void mitk::CameraIntrinsics::SetDistorsionCoeffs( double k1, double k2, double p1, double p2 ) { { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); m_DistorsionCoeffs.at(0,0) = k1; m_DistorsionCoeffs.at(0,1) = k2; m_DistorsionCoeffs.at(0,2) = p1; m_DistorsionCoeffs.at(0,3) = p2; } this->Modified(); } cv::Mat mitk::CameraIntrinsics::GetCameraMatrix() const { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); return m_CameraMatrix.clone(); // return a copy of this small matrix } cv::Mat mitk::CameraIntrinsics::GetDistorsionCoeffs() const { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); return m_DistorsionCoeffs.clone(); // return a copy of this small matrix } cv::Mat mitk::CameraIntrinsics::GetDistorsionCoeffs() { const CameraIntrinsics* intrinsics = this; return intrinsics->GetDistorsionCoeffs(); } std::string mitk::CameraIntrinsics::ToString() const { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); std::ostringstream s; s.precision(12); const cv::Mat& CameraMatrix = m_CameraMatrix; const cv::Mat& DistorsionCoeffs = m_DistorsionCoeffs; s.str(""); s << this->GetNameOfClass() << ": "; s << "fx = " << CameraMatrix.at(0,0); s << ", fy = " << CameraMatrix.at(1,1); s << ", cx = " << CameraMatrix.at(0,2); s << ", cy = " << CameraMatrix.at(1,2); s << ", k1 = " << DistorsionCoeffs.at(0,0); s << ", k2 = " << DistorsionCoeffs.at(0,1); s << ", p1 = " << DistorsionCoeffs.at(0,2); s << ", p2 = " << DistorsionCoeffs.at(0,3); //s << ", k3 = " << DistorsionCoeffs.at(0,4); return s.str(); } void mitk::CameraIntrinsics::ToXML(tinyxml2::XMLElement* elem) const { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); elem->SetValue(this->GetNameOfClass()); std::ostringstream s; s.precision(12); const cv::Mat& CameraMatrix = m_CameraMatrix; s.str(""); s << CameraMatrix.at(0,0); elem->SetAttribute( "fx", s.str().c_str() ); s.str(""); s << CameraMatrix.at(1,1); elem->SetAttribute( "fy", s.str().c_str()); s.str(""); s << CameraMatrix.at(0,2); elem->SetAttribute( "cx", s.str().c_str()); s.str(""); s << CameraMatrix.at(1,2); elem->SetAttribute( "cy", s.str().c_str()); const cv::Mat& DistorsionCoeffs = m_DistorsionCoeffs; s.str(""); s << DistorsionCoeffs.at(0,0); elem->SetAttribute( "k1", s.str().c_str()); s.str(""); s << DistorsionCoeffs.at(0,1); elem->SetAttribute( "k2", s.str().c_str()); s.str(""); s << DistorsionCoeffs.at(0,2); elem->SetAttribute( "p1", s.str().c_str()); s.str(""); s << DistorsionCoeffs.at(0,3); elem->SetAttribute( "p2", s.str().c_str()); elem->SetAttribute("Valid", static_cast(m_Valid)); //s.str(""); s << DistorsionCoeffs.at(4,0); //elem->SetAttribute( "k3", s.str().c_str() ); } void mitk::CameraIntrinsics::FromGMLCalibrationXML(const tinyxml2::XMLElement* elem) { assert( elem ); assert( std::string(elem->Value()) == "results" ); cv::Mat CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType::type); CameraMatrix.at(2,2) = 1.0; cv::Mat DistorsionCoeffs = cv::Mat::zeros(1, 5, cv::DataType::type); const auto* focus_lenXElem = elem->FirstChildElement("focus_lenX"); endoAssert( focus_lenXElem != nullptr ); CameraMatrix.at(0,0) = atof( focus_lenXElem->GetText() ); const auto* focus_lenYElem = elem->FirstChildElement("focus_lenY"); endoAssert( focus_lenYElem != nullptr ); CameraMatrix.at(1,1) = atof( focus_lenYElem->GetText() ); const auto* PrincipalXElem = elem->FirstChildElement("PrincipalX"); endoAssert( PrincipalXElem != nullptr ); CameraMatrix.at(0,2) = atof( PrincipalXElem->GetText() ); const auto* PrincipalYElem = elem->FirstChildElement("PrincipalY"); endoAssert( PrincipalYElem != nullptr ); CameraMatrix.at(1,2) = atof( PrincipalYElem->GetText() ); // DISTORSION COEFFS const auto* Dist1Elem = elem->FirstChildElement("Dist1"); endoAssert( Dist1Elem != nullptr ); DistorsionCoeffs.at(0,0) = atof( Dist1Elem->GetText() ); const auto* Dist2Elem = elem->FirstChildElement("Dist2"); endoAssert( Dist2Elem != nullptr ); DistorsionCoeffs.at(0,1) = atof( Dist2Elem->GetText() ); const auto* Dist3Elem = elem->FirstChildElement("Dist3"); endoAssert( Dist3Elem != nullptr ); DistorsionCoeffs.at(0,2) = atof( Dist3Elem->GetText() ); const auto* Dist4Elem = elem->FirstChildElement("Dist4"); endoAssert( Dist4Elem != nullptr ); DistorsionCoeffs.at(0,3) = atof( Dist4Elem->GetText() ); int valid = 0; elem->QueryIntAttribute("Valid", &valid); { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); m_Valid = static_cast(valid); m_CameraMatrix = CameraMatrix; m_DistorsionCoeffs = DistorsionCoeffs; } this->Modified(); } void mitk::CameraIntrinsics::FromXML(const tinyxml2::XMLElement* elem) { endoAssert ( elem ); MITK_DEBUG << elem->Value(); const char* filename = elem->Attribute("file"); if(nullptr != filename) { this->FromXMLFile(filename); return; } else if(strcmp(elem->Value(), "CalibrationProject") == 0) { this->FromGMLCalibrationXML(elem->FirstChildElement("results")); return; } assert ( elem ); if(strcmp(elem->Value(), this->GetNameOfClass()) != 0) elem = elem->FirstChildElement(this->GetNameOfClass()); std::ostringstream err; // CAMERA MATRIX cv::Mat CameraMatrix = cv::Mat::zeros(3, 3, cv::DataType::type); CameraMatrix.at(2,2) = 1.0; double val = 0.0; if(elem->QueryDoubleAttribute("fx", &val) == tinyxml2::XML_SUCCESS) CameraMatrix.at(0,0) = val; else err << "fx, "; if(elem->QueryDoubleAttribute("fy", &val) == tinyxml2::XML_SUCCESS) CameraMatrix.at(1,1) = val; else err << "fy, "; if(elem->QueryDoubleAttribute("cx", &val) == tinyxml2::XML_SUCCESS) CameraMatrix.at(0,2) = val; else err << "cx, "; if(elem->QueryDoubleAttribute("cy", &val) == tinyxml2::XML_SUCCESS) CameraMatrix.at(1,2) = val; else err << "cy, "; // DISTORSION COEFFS endodebug( "creating DistorsionCoeffs from XML file") cv::Mat DistorsionCoeffs = cv::Mat::zeros(1, 5, cv::DataType::type); if(elem->QueryDoubleAttribute("k1", &val) == tinyxml2::XML_SUCCESS) DistorsionCoeffs.at(0,0) = val; else err << "k1, "; if(elem->QueryDoubleAttribute("k2", &val) == tinyxml2::XML_SUCCESS) DistorsionCoeffs.at(0,1) = val; else err << "k2, "; if(elem->QueryDoubleAttribute("p1", &val) == tinyxml2::XML_SUCCESS) DistorsionCoeffs.at(0,2) = val; else err << "p1, "; if(elem->QueryDoubleAttribute("p2", &val) == tinyxml2::XML_SUCCESS) DistorsionCoeffs.at(0,3) = val; else err << "p2, "; DistorsionCoeffs.at(0,4) = 0.0; /*if(elem->QueryDoubleAttribute("k3", &val) == tinyxml2::XML_SUCCESS) DistorsionCoeffs.at(4,0) = val; else err << "k3, ";*/ std::string errorStr = err.str(); int errLength = errorStr.length(); if(errLength > 0) { errorStr = errorStr.substr(0, errLength-2); errorStr.append(" not found"); throw std::invalid_argument(err.str()); } int valid = 0; elem->QueryIntAttribute("Valid", &valid); { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); m_Valid = static_cast(valid); m_CameraMatrix = CameraMatrix; m_DistorsionCoeffs = DistorsionCoeffs; } this->Modified(); } double mitk::CameraIntrinsics::GetFocalLengthX() const { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); double FocalLengthX = m_CameraMatrix.at(0,0); return FocalLengthX; } double mitk::CameraIntrinsics::GetFocalLengthY() const { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); double FocalLengthY = m_CameraMatrix.at(1,1);; return FocalLengthY; } double mitk::CameraIntrinsics::GetPrincipalPointX() const { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); double PrincipalPointX = m_CameraMatrix.at(0,2); return PrincipalPointX; } double mitk::CameraIntrinsics::GetPrincipalPointY() const { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); double PrincipalPointY = m_CameraMatrix.at(1,2); return PrincipalPointY; } mitk::Point4D mitk::CameraIntrinsics::GetDistorsionCoeffsAsPoint4D() const { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); mitk::Point4D coeffs; coeffs[0] = m_DistorsionCoeffs.at(0,0); coeffs[1] = m_DistorsionCoeffs.at(0,1); coeffs[2] = m_DistorsionCoeffs.at(0,2); coeffs[3] = m_DistorsionCoeffs.at(0,3); return coeffs; } mitk::Point3D mitk::CameraIntrinsics::GetFocalPoint() const { mitk::Point3D p; p[0] = this->GetFocalLengthX(); p[1] = this->GetFocalLengthY(); p[2] = 0; return p; } mitk::Point3D mitk::CameraIntrinsics::GetPrincipalPoint() const { mitk::Point3D p; p[0] = this->GetPrincipalPointX(); p[1] = this->GetPrincipalPointY(); p[2] = 0; return p; } vnl_vector_fixed mitk::CameraIntrinsics::GetFocalPointAsVnlVector() const { vnl_vector_fixed vec; vec[0] = this->GetFocalLengthX(); vec[1] = this->GetFocalLengthY(); return vec; } vnl_vector_fixed mitk::CameraIntrinsics::GetPrincipalPointAsVnlVector() const { vnl_vector_fixed vec; vec[0] = this->GetPrincipalPointX(); vec[1] = this->GetPrincipalPointY(); return vec; } std::ostream& operator<< (std::ostream& os, mitk::CameraIntrinsics::Pointer p) { os << p->ToString(); return os; } std::string mitk::CameraIntrinsics::GetString() { return this->ToString(); } std::string mitk::CameraIntrinsics::ToOctaveString( const std::string& varName) { std::ostringstream s; s << varName << " = [" << this->GetFocalLengthX() << " 0 " << this->GetPrincipalPointX() << "; 0 " << this->GetFocalLengthY() << " " << this->GetPrincipalPointY() << ";" << " 0 0 1 ];"; return s.str(); } void mitk::CameraIntrinsics::SetValid( bool valid ) { - itk::MutexLockHolder lock(*m_Mutex); + std::lock_guard lock(m_Mutex); m_Valid = valid; } itk::LightObject::Pointer mitk::CameraIntrinsics::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/CameraCalibration/mitkCameraIntrinsics.h b/Modules/CameraCalibration/mitkCameraIntrinsics.h index a36cbf0a7f..77c7a9aee7 100644 --- a/Modules/CameraCalibration/mitkCameraIntrinsics.h +++ b/Modules/CameraCalibration/mitkCameraIntrinsics.h @@ -1,141 +1,142 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitkCameraIntrinsics_h #define mitkCameraIntrinsics_h #include #include #include -#include #include #include "mitkXMLSerializable.h" #include #include "opencv2/core.hpp" +#include + int mitkCameraIntrinsicsTest(int, char* []); namespace mitk { /// /// \brief class representing camera intrinsics and related functions /// class MITKCAMERACALIBRATION_EXPORT CameraIntrinsics: virtual public itk::Object, virtual public mitk::XMLSerializable { public: /// /// for testing purposes /// friend int mitkCameraIntrinsicsTest(int argc, char* argv[]); /// /// smartpointer typedefs /// mitkClassMacroItkParent(CameraIntrinsics, itk::Object); /// /// the static new function /// itkFactorylessNewMacro(Self); /// /// make a clone of this intrinsics /// itkCloneMacro(Self); /// /// copy information from other to this /// void Copy(const CameraIntrinsics* other); /// /// checks two intrinsics for equality /// bool Equals( const CameraIntrinsics* other ) const; /// /// \return the intrinsic parameter matrix as a 3x3 vnl matrix /// vnl_matrix_fixed GetVnlCameraMatrix() const; /// /// \return the intrinsic parameter matrix as a 3x4 vnl matrix /// (the last column only containing zeros) /// vnl_matrix_fixed GetVnlCameraMatrix3x4() const; /// /// \return true if the intrinsics are set (some plausibility checks /// may be done here) /// bool IsValid() const; void SetValid(bool valid); cv::Mat GetCameraMatrix() const; cv::Mat GetDistorsionCoeffs(); cv::Mat GetDistorsionCoeffs() const; void ToXML(tinyxml2::XMLElement* elem) const override; std::string ToString() const; std::string GetString(); double GetFocalLengthX() const; double GetFocalLengthY() const; double GetPrincipalPointX() const; double GetPrincipalPointY() const; mitk::Point4D GetDistorsionCoeffsAsPoint4D() const; mitk::Point3D GetFocalPoint() const; mitk::Point3D GetPrincipalPoint() const; vnl_vector_fixed GetFocalPointAsVnlVector() const; vnl_vector_fixed GetPrincipalPointAsVnlVector() const; /// /// set a new camera matrix utilizing a vnl matrix /// void SetCameraMatrix( const vnl_matrix_fixed& _CameraMatrix ); void SetIntrinsics( const cv::Mat& _CameraMatrix , const cv::Mat& _DistorsionCoeffs); void SetFocalLength( double x, double y ); void SetPrincipalPoint( double x, double y ); void SetDistorsionCoeffs( double k1, double k2, double p1, double p2 ); void SetIntrinsics( const mitk::Point3D& focalPoint, const mitk::Point3D& principalPoint, const mitk::Point4D& distortionCoefficients); void FromXML(const tinyxml2::XMLElement* elem) override; void FromGMLCalibrationXML(const tinyxml2::XMLElement* elem); std::string ToOctaveString(const std::string& varName="CameraIntrinsics"); ~CameraIntrinsics() override; protected: CameraIntrinsics(); CameraIntrinsics(const CameraIntrinsics& other); cv::Mat m_CameraMatrix; cv::Mat m_DistorsionCoeffs; bool m_Valid; - itk::FastMutexLock::Pointer m_Mutex; + mutable std::mutex m_Mutex; private: itk::LightObject::Pointer InternalClone() const override; }; } // namespace mitk MITKCAMERACALIBRATION_EXPORT std::ostream& operator<< (std::ostream& os, mitk::CameraIntrinsics::Pointer p); #endif // mitkCameraIntrinsics_h diff --git a/Modules/CameraCalibration/mitkEndoDebug.cpp b/Modules/CameraCalibration/mitkEndoDebug.cpp index f86b018e70..3a62e0ce2b 100644 --- a/Modules/CameraCalibration/mitkEndoDebug.cpp +++ b/Modules/CameraCalibration/mitkEndoDebug.cpp @@ -1,291 +1,252 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkEndoDebug.h" #include -#include -#include +#include #include #include #include namespace mitk { struct EndoDebugData { EndoDebugData() : m_DebugEnabled(false) , m_ShowImagesInDebug(false) , m_ShowImagesTimeOut(false) - , m_Mutex(itk::FastMutexLock::New()) , m_DebugImagesOutputDirectory("") { } std::set m_FilesToDebug; std::set m_SymbolsToDebug; bool m_DebugEnabled; bool m_ShowImagesInDebug; size_t m_ShowImagesTimeOut; std::ofstream m_Stream; - itk::FastMutexLock::Pointer m_Mutex; + std::mutex m_Mutex; std::string m_DebugImagesOutputDirectory; }; EndoDebug::EndoDebug() : d ( new EndoDebugData ) { } EndoDebug::~EndoDebug() { if(d->m_Stream.is_open()) d->m_Stream.close(); delete d; } EndoDebug& EndoDebug::GetInstance() { static EndoDebug instance; return instance; } std::string EndoDebug::GetUniqueFileName( const std::string& dir, const std::string& ext, const std::string& prefix ) { std::stringstream s; s.precision( 0 ); std::string filename; int i = 0; while( filename.empty() || itksys::SystemTools::FileExists( (dir+"/"+filename).c_str() ) ) { s.str(""); s << i; filename = prefix + s.str() + "." + ext; ++i; } filename = dir+"/"+filename; return filename; } std::string EndoDebug::GetFilenameWithoutExtension(const std::string& s) { return itksys::SystemTools::GetFilenameWithoutExtension( s ); } bool EndoDebug::AddFileToDebug(const std::string& s) { - { - itk::MutexLockHolder lock(*d->m_Mutex); - std::pair::iterator, bool> res = d->m_FilesToDebug.insert( s ); - return res.second; - } + std::lock_guard lock(d->m_Mutex); + std::pair::iterator, bool> res = d->m_FilesToDebug.insert( s ); + return res.second; } void EndoDebug::SetFilesToDebug(const std::set &filesToDebug) { - { - itk::MutexLockHolder lock(*d->m_Mutex); - d->m_FilesToDebug = filesToDebug; - } + std::lock_guard lock(d->m_Mutex); + d->m_FilesToDebug = filesToDebug; } std::set EndoDebug::GetFilesToDebug() { - { - itk::MutexLockHolder lock(*d->m_Mutex); - return d->m_FilesToDebug; - } + std::lock_guard lock(d->m_Mutex); + return d->m_FilesToDebug; } bool EndoDebug::AddSymbolToDebug(const std::string& symbolToDebug) { - { - itk::MutexLockHolder lock(*d->m_Mutex); - std::pair::iterator, bool> res = d->m_SymbolsToDebug.insert( symbolToDebug ); - return res.second; - } + std::lock_guard lock(d->m_Mutex); + std::pair::iterator, bool> res = d->m_SymbolsToDebug.insert( symbolToDebug ); + return res.second; } void EndoDebug::SetSymbolsToDebug(const std::set &symbolsToDebug) { - { - itk::MutexLockHolder lock(*d->m_Mutex); - d->m_SymbolsToDebug = symbolsToDebug; - } + std::lock_guard lock(d->m_Mutex); + d->m_SymbolsToDebug = symbolsToDebug; } std::set EndoDebug::GetSymbolsToDebug() { - { - itk::MutexLockHolder lock(*d->m_Mutex); - return d->m_SymbolsToDebug; - } + std::lock_guard lock(d->m_Mutex); + return d->m_SymbolsToDebug; } bool EndoDebug::DebugSymbol(const std::string& s) { - { - itk::MutexLockHolder lock(*d->m_Mutex); - return d->m_SymbolsToDebug.find(s) - != d->m_SymbolsToDebug.end(); - } + std::lock_guard lock(d->m_Mutex); + return d->m_SymbolsToDebug.find(s) + != d->m_SymbolsToDebug.end(); } bool EndoDebug::DebugFile(const std::string& s) { std::string filename = GetFilenameWithoutExtension(s); - { - itk::MutexLockHolder lock(*d->m_Mutex); - return d->m_FilesToDebug.find(filename) - != d->m_FilesToDebug.end(); - } + std::lock_guard lock(d->m_Mutex); + return d->m_FilesToDebug.find(filename) + != d->m_FilesToDebug.end(); } bool EndoDebug::Debug( const std::string& fileToDebug, const std::string& symbol ) { bool debug = false; { bool debugEnabled = false; size_t filesSize = 0; size_t symbolsSize = 0; bool symbolFound = false; { - itk::MutexLockHolder lock(*d->m_Mutex); + std::lock_guard lock(d->m_Mutex); debugEnabled = d->m_DebugEnabled; filesSize = d->m_FilesToDebug.size(); symbolsSize = d->m_SymbolsToDebug.size(); symbolFound = d->m_SymbolsToDebug.find(symbol) != d->m_SymbolsToDebug.end(); } if( debugEnabled ) { if( filesSize == 0 ) debug = true; else debug = DebugFile(fileToDebug); // ok debug is determined so far, now check if symbol set if( symbolsSize > 0 ) { debug = symbolFound; } else { // do not show symbol debug output if no symbols are set at all if( !symbol.empty() ) debug = false; } } } return debug; } void EndoDebug::SetDebugEnabled(bool _DebugEnabled) { - { - itk::MutexLockHolder lock(*d->m_Mutex); - d->m_DebugEnabled = _DebugEnabled; - } + std::lock_guard lock(d->m_Mutex); + d->m_DebugEnabled = _DebugEnabled; } void EndoDebug::SetDebugImagesOutputDirectory(const std::string& _DebugImagesOutputDirectory) { - { - itk::MutexLockHolder lock(*d->m_Mutex); - d->m_DebugImagesOutputDirectory = _DebugImagesOutputDirectory; - } - + std::lock_guard lock(d->m_Mutex); + d->m_DebugImagesOutputDirectory = _DebugImagesOutputDirectory; } bool EndoDebug::GetDebugEnabled() { - { - itk::MutexLockHolder lock(*d->m_Mutex); - return d->m_DebugEnabled; - } + std::lock_guard lock(d->m_Mutex); + return d->m_DebugEnabled; } void EndoDebug::SetShowImagesInDebug(bool _ShowImagesInDebug) { - { - itk::MutexLockHolder lock(*d->m_Mutex); - d->m_ShowImagesInDebug = _ShowImagesInDebug; - } + std::lock_guard lock(d->m_Mutex); + d->m_ShowImagesInDebug = _ShowImagesInDebug; } bool EndoDebug::GetShowImagesInDebug() { - { - itk::MutexLockHolder lock(*d->m_Mutex); - return d->m_ShowImagesInDebug; - } + std::lock_guard lock(d->m_Mutex); + return d->m_ShowImagesInDebug; } void EndoDebug::SetShowImagesTimeOut(size_t _ShowImagesTimeOut) { - { - itk::MutexLockHolder lock(*d->m_Mutex); - d->m_ShowImagesTimeOut = _ShowImagesTimeOut; - } + std::lock_guard lock(d->m_Mutex); + d->m_ShowImagesTimeOut = _ShowImagesTimeOut; } std::string EndoDebug::GetDebugImagesOutputDirectory() const { - { - itk::MutexLockHolder lock(*d->m_Mutex); - return d->m_DebugImagesOutputDirectory; - } + std::lock_guard lock(d->m_Mutex); + return d->m_DebugImagesOutputDirectory; } size_t EndoDebug::GetShowImagesTimeOut() { - { - itk::MutexLockHolder lock(*d->m_Mutex); - return d->m_ShowImagesTimeOut; - } + std::lock_guard lock(d->m_Mutex); + return d->m_ShowImagesTimeOut; } void EndoDebug::SetLogFile( const std::string& file ) { - { - itk::MutexLockHolder lock(*d->m_Mutex); - d->m_Stream.open ( file.c_str(), std::ios::out | std::ios::app); - } + std::lock_guard lock(d->m_Mutex); + d->m_Stream.open ( file.c_str(), std::ios::out | std::ios::app); } void EndoDebug::ShowMessage( const std::string& message ) { + std::lock_guard lock(d->m_Mutex); + if(d->m_Stream.is_open()) { - itk::MutexLockHolder lock(*d->m_Mutex); - if(d->m_Stream.is_open()) - { - char *timestr; - struct tm *newtime; - time_t aclock; - time(&aclock); - newtime = localtime(&aclock); - timestr = asctime(newtime); - - d->m_Stream << timestr << ", " << message; - } - else - std::cout << message << std::flush; + char *timestr; + struct tm *newtime; + time_t aclock; + time(&aclock); + newtime = localtime(&aclock); + timestr = asctime(newtime); + + d->m_Stream << timestr << ", " << message; } + else + std::cout << message << std::flush; } } diff --git a/Modules/CameraCalibration/mitkTransform.cpp b/Modules/CameraCalibration/mitkTransform.cpp index 0a65753afb..c0966266d8 100644 --- a/Modules/CameraCalibration/mitkTransform.cpp +++ b/Modules/CameraCalibration/mitkTransform.cpp @@ -1,750 +1,750 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkTransform.h" #include #include #include #include #include #include #include namespace mitk { // DO NOT CHANGE THE VALUES OF THESE CONSTANTS!! const std::string Transform::UNKNOWN_TYPE = "Unknown type"; const std::string Transform::ENDOSCOPE_SCOPE_TOOL = "Endoscope scope tool"; const std::string Transform::ENDOSCOPE_CAM_TOOL = "Endoscope camera tool"; const std::string Transform::CHESSBOARD_TOOL = "Chessboard tool"; const std::string Transform::POINTER_TOOL = "Pointer tool"; const std::string Transform::POINTER_TO_CHESSBOARD_ORIGIN = "Pointer to chessboard origin"; const std::string Transform::POINTER_TO_CHESSBOARD_X_SUPPORT_POINT = "Pointer to chessboard X support origin"; const std::string Transform::POINTER_TO_CHESSBOARD_Y_SUPPORT_POINT = "Pointer to chessboard Y support origin"; const std::string Transform::BOARD_TO_BOARD_TOOL = "Board to board tool"; const std::string Transform::REFERENCE_CAMERA_TRANSFORM = "Reference camera transform"; const std::string Transform::REFERENCE_SCOPE_TRANSFORM = "Reference scope transform"; const std::string Transform::EYE_TO_HAND_TRANSFORM = "Eye to hand transform"; const std::string Transform::CAMERA_EXTRINSICS = "Camera extrinsics"; Transform::Transform() : m_NavData(mitk::NavigationData::New()), m_Type( UNKNOWN_TYPE ) { vnl_matrix_fixed rot; rot.set_identity(); this->SetRotation( rot ); } Transform::Transform(const mitk::NavigationData* nd) : m_NavData(mitk::NavigationData::New()), m_Type( UNKNOWN_TYPE ) { m_NavData->Graft(nd); } Transform::Transform(const std::string& s) : m_NavData(mitk::NavigationData::New()), m_Type( s ) { vnl_matrix_fixed rot; rot.set_identity(); this->SetRotation( rot ); } void Transform::Copy(const mitk::NavigationData* nd) { (const_cast(m_NavData.GetPointer()))->Graft(nd); } void Transform::Concatenate( mitk::Transform* transform ) { vnl_matrix_fixed mat = transform->GetMatrix(); mat = mat * this->GetMatrix(); // this->SetMatrix( mat ); } void Transform::Concatenate( const vnl_matrix_fixed& transform ) { Transform::Pointer t = Transform::New(); t->SetMatrix( transform ); this->Concatenate( t ); } void Transform::Concatenate( const vtkMatrix4x4* transform ) { Transform::Pointer t = Transform::New(); t->SetMatrix( transform ); this->Concatenate( t ); } void Transform::Reset() { mitk::NavigationData::Pointer nd = NavigationData::New(); this->Copy( nd ); } void Transform::SetOrientation( const vnl_quaternion& orientation) { m_NavData->SetOrientation(orientation); this->Modified(); } void Transform::SetTranslation( const vnl_vector_fixed& transl) { mitk::Point3D p; for(unsigned int i=0; i<3; ++i) p[i] = transl[i]; m_NavData->SetPosition(p); this->Modified(); } void Transform::SetTranslation( float* array ) { vnl_vector_fixed vec; for(unsigned int i=0; iSetTranslation( vec ); } void Transform::SetRotation( float* array ) { vnl_matrix_fixed mat; unsigned int row = 0; unsigned int col = 0; for(unsigned int i=0; i 0 && i % 3 == 0 ) { ++row; col = 0; } mat(row,col) = array[i]; ++col; } this->SetRotation( mat ); } void Transform::SetOrientation( const vnl_quaternion& orientation) { vnl_vector_fixed qvec; VnlVectorFixedCaster caster( &orientation, &qvec ); caster.Update(); mitk::Quaternion p( qvec ); this->SetOrientation( p ); } vnl_vector_fixed Transform::GetVnlDoubleTranslation() const { vnl_vector_fixed vecFloat = this->GetVnlTranslation(); vnl_vector_fixed vecDouble; VnlVectorFixedCaster caster( &vecFloat, &vecDouble ); caster.Update(); return vecDouble; } void Transform::SetTranslation( const vnl_vector& transl) { vnl_vector_fixed dTransl(transl); vnl_vector_fixed fTransl; VnlVectorFixedCaster caster( &dTransl, &fTransl ); caster.Update(); this->SetTranslation( fTransl ); } vnl_quaternion Transform::GetVnlDoubleQuaternion() const { mitk::Quaternion fOrientation = this->GetOrientation(); vnl_quaternion dOrientation; VnlVectorFixedCaster caster( &fOrientation, &dOrientation ); caster.Update(); return dOrientation; } void Transform::FromCSVFile(const std::string& file) { std::ifstream csvFile (file.c_str()); endoAssert ( csvFile.fail() == false ); mitk::Transform::Pointer transform = mitk::Transform::New(); vnl_matrix_fixed mat; std::string line; mitk::ScalarType d = 0.0f; int row=0,column = 0; while (std::getline (csvFile, line)) { std::istringstream linestream(line); std::string item; column = 0; while (std::getline (linestream, item, ',')) { std::istringstream number; number.str(item); number >> d; mat(row, column) = d; ++column; } ++row; } endoAssert( row == 4 && column == 4 ); transform->SetMatrix( mat ); this->SetNavigationData( transform->GetNavigationData() ); // modified is called in SetNavigationData } std::string Transform::ToCSVString() const { std::ostringstream s; s.precision(12); vnl_matrix_fixed mat = this->GetMatrix(); for( unsigned int j=0; j mat = this->GetMatrix(); s << varname << " = ["; for( unsigned int j=0; jGraft(transform->GetNavigationData()); m_Type = transform->GetType(); } mitk::Transform::Pointer Transform::Clone() const { Transform::Pointer copy = Transform::New(); copy->Copy( this ); return copy; } void Transform::SetMatrix( const vtkMatrix4x4* mat) { vnl_matrix_fixed vnlMat; for(unsigned int i=0; i<4; ++i) for(unsigned int j=0; j<4; ++j) vnlMat(i,j) = mat->GetElement(i, j); this->SetMatrix( vnlMat ); } void Transform::ToCSVFile(const std::string& file) const { std::ofstream csvFile; csvFile.open(file.c_str()); endoAssert ( csvFile.fail() == false ); csvFile << this->ToCSVString(); csvFile.close(); } void Transform::ToMatlabFile(const std::string& file , const std::string& varname) const { std::ofstream csvFile; csvFile.open(file.c_str()); endoAssert ( csvFile.fail() == false ); csvFile << this->ToMatlabString(varname); csvFile.close(); } void Transform::SetNavigationData( const mitk::NavigationData* naviData ) { endoAssert( naviData != nullptr ); m_NavData->Graft( naviData ); this->Modified(); } void Transform::SetRotation( vnl_matrix_fixed& mat) { this->m_NavData->SetOrientation( mitk::Quaternion(mat) ); this->Modified(); } void Transform::SetRotation( vnl_matrix& mat) { vnl_matrix_fixed tmp(mat); this->SetRotation( tmp ); } void Transform::SetPosition( const mitk::Point3D& transl) { this->SetTranslation( transl.GetVnlVector() ); } void Transform::SetTranslation( double array[3] ) { mitk::Point3D p; for(unsigned int i = 0; i < 3; ++i) p.SetElement(i, array[i]); this->SetTranslation( p.GetVnlVector() ); } void Transform::SetRotation( double array[3][3] ) { vnl_matrix_fixed mat; for(unsigned int i = 0; i < 3; ++i) for(unsigned int j = 0; j < 3; ++j) mat(i, j) = array[i][j]; this->SetRotation( mat ); } void Transform::Invert() { vnl_matrix_fixed tmp(this->GetMatrix()); this->SetMatrix( vnl_inverse( tmp ) ); } void Transform::SetMatrix( const vnl_matrix_fixed& mat) { // set translation first - vnl_vector transl = mat.get_column(3); + vnl_vector transl = mat.get_column(3).as_ref(); mitk::Point3D p; for(unsigned int i=0; i<3; ++i) p[i] = transl[i]; m_NavData->SetPosition(p); // set rotation vnl_matrix_fixed rotMatFixed( mat.extract(3,3)); this->SetRotation(rotMatFixed); } bool Transform::IsValid() const { return m_NavData->IsDataValid(); } void Transform::SetTranslation( const cv::Mat& transl) { vnl_vector vec(3); VnlVectorFromCvMat _VnlVectorFromCvMat( &transl, &vec ); _VnlVectorFromCvMat.Update(); this->SetTranslation( vnl_vector_fixed( vec ) ); } void Transform::SetRotation( const cv::Mat& mat ) { vnl_matrix vnlMat(3, 3); VnlMatrixFromCvMat _VnlMatrixFromCvMat( &mat, &vnlMat ); _VnlMatrixFromCvMat.Update(); vnl_matrix_fixed vnlMatFixed(vnlMat); this->SetRotation(vnlMatFixed); } void Transform::SetRotationVector( const cv::Mat& rotVec ) { cv::Mat rotMat; cv::Rodrigues( rotVec, rotMat ); vnl_matrix vnlMat(3, 3); VnlMatrixFromCvMat _VnlMatrixFromCvMat( &rotMat, &vnlMat ); _VnlMatrixFromCvMat.Update(); vnl_matrix_fixed vnlMatFixed(vnlMat); this->SetRotation( vnlMatFixed ); } //# getter mitk::NavigationData::Pointer Transform::GetNavigationData() const { return m_NavData; } mitk::Point3D Transform::GetTranslation() const { return m_NavData->GetPosition(); } mitk::Point3D Transform::GetPosition() const { return m_NavData->GetPosition(); } mitk::Quaternion Transform::GetOrientation() const { return m_NavData->GetOrientation(); } void Transform::GetMatrix(vtkMatrix4x4* matrix) const { vnl_matrix_fixed vnlMat = this->GetMatrix(); for(unsigned int i=0; iSetElement(i,j, vnlMat(i,j)); } void Transform::GetVtkOpenGlMatrix(vtkMatrix4x4* matrix) const { vnl_matrix vnlRotation = this->GetVnlRotationMatrix().as_matrix(); // normalize rows of rotation matrix vnlRotation.normalize_rows(); vnl_matrix vnlInverseRotation(3,3); // invert rotation - vnlInverseRotation = vnl_matrix_inverse(vnlRotation); + vnlInverseRotation = vnl_matrix_inverse(vnlRotation.as_ref()).as_matrix(); vnl_vector vnlTranslation = this->GetPosition().GetVnlVector(); // rotate translation vector by inverse rotation P = P' vnlTranslation = vnlInverseRotation * vnlTranslation; vnlTranslation *= -1; // save -P' // set position mitk::Transform::Pointer tmp = mitk::Transform::New(); tmp->SetTranslation( vnlTranslation ); tmp->SetRotation( vnlRotation ); tmp->GetMatrix(matrix); } mitk::Point3D Transform::TransformPoint(mitk::Point3D point) const { itk::Matrix R(GetVnlRotationMatrix()); itk::Point pointR = (R * point); mitk::Point3D retPoint = pointR; retPoint[0] = pointR[0] + GetPosition()[0]; retPoint[1] = pointR[1] + GetPosition()[1]; retPoint[2] = pointR[2] + GetPosition()[2]; return retPoint; } //# cv getter cv::Mat Transform::GetCvTranslation() const { cv::Mat mat; vnl_vector vec = this->GetVnlTranslation().as_vector(); endodebugvar( vec ) CvMatFromVnlVector _CvMatFromVnlVector(&vec, &mat); _CvMatFromVnlVector.Update(); return mat; } cv::Mat Transform::GetCvRotationMatrix() const { cv::Mat mat; vnl_matrix vec = this->GetVnlRotationMatrix().as_matrix(); endodebugvar( vec ) CvMatFromVnlMatrix _CvMatFromVnlMatrix(&vec, &mat); _CvMatFromVnlMatrix.Update(); return mat; } cv::Mat Transform::GetCvMatrix() const { cv::Mat mat; vnl_matrix vec = this->GetMatrix().as_matrix(); CvMatFromVnlMatrix _CvMatFromVnlMatrix(&vec, &mat); _CvMatFromVnlMatrix.Update(); return mat; } cv::Mat Transform::GetCvRotationVector() const { cv::Mat rotVec(3,1,cv::DataType::type); cv::Rodrigues( this->GetCvRotationMatrix(), rotVec ); return rotVec; } //# vnl getter vnl_vector_fixed Transform::GetVnlTranslation() const { vnl_vector_fixed vec(m_NavData->GetPosition() .GetVnlVector()); return vec; } vnl_matrix_fixed Transform::GetVnlRotationMatrix() const { return m_NavData->GetOrientation().rotation_matrix_transpose(); } vnl_matrix_fixed Transform::GetVnlDoubleMatrix() const { vnl_matrix_fixed mat = this->GetMatrix(); vnl_matrix_fixed doubleMat; for(unsigned int i=0; i( mat(i,j) ); return doubleMat; } vnl_matrix_fixed Transform::GetMatrix() const { vnl_vector_fixed transl = this->GetVnlTranslation(); vnl_matrix_fixed rot = this->GetVnlRotationMatrix(); vnl_matrix_fixed homMat; homMat.set_identity(); //std::cout << homMat << std::endl; for(unsigned int i=0; i rotMat = this->GetVnlRotationMatrix().transpose(); this->SetRotation( rotMat ); } void Transform::SetValid( bool valid ) { if( m_NavData->IsDataValid() == valid ) return; m_NavData->SetDataValid( valid ); this->Modified(); } std::string mitk::Transform::ToString() const { std::ostringstream s; s.precision(12); mitk::NavigationData::PositionType position; position.Fill(0.0); position = m_NavData->GetPosition(); mitk::NavigationData::OrientationType orientation(0.0, 0.0, 0.0, 0.0); orientation = m_NavData->GetOrientation(); s << "Translation: [" << position[0] << ", " << position[1] << ", " << position[2] << "]"; s << ", orientation: [" << orientation[0] << ", " << orientation[1] << ", " << orientation[2] << ", " << orientation[3] << "]"; s << ", valid: [" << (this->IsValid()? "true": "false") << "]"; return s.str(); } void mitk::Transform::ToXML(tinyxml2::XMLElement* elem) const { std::string value = elem->Value() != nullptr ? elem->Value() : ""; if(value.empty()) elem->SetValue(this->GetNameOfClass()); mitk::NavigationData::PositionType position; position.Fill(0.0); position = m_NavData->GetPosition(); mitk::NavigationData::OrientationType orientation(0.0, 0.0, 0.0, 0.0); orientation = m_NavData->GetOrientation(); mitk::NavigationData::CovarianceMatrixType matrix; matrix.SetIdentity(); matrix = m_NavData->GetCovErrorMatrix(); bool hasPosition = true; hasPosition = m_NavData->GetHasPosition(); bool hasOrientation = true; hasOrientation = m_NavData->GetHasOrientation(); bool dataValid = false; dataValid = m_NavData->IsDataValid(); mitk::NavigationData::TimeStampType timestamp=0.0; elem->SetAttribute("Type", m_Type.c_str()); elem->SetAttribute("Time", timestamp); elem->SetAttribute("X", position[0]); elem->SetAttribute("Y", position[1]); elem->SetAttribute("Z", position[2]); elem->SetAttribute("QX", orientation[0]); elem->SetAttribute("QY", orientation[1]); elem->SetAttribute("QZ", orientation[2]); elem->SetAttribute("QR", orientation[3]); elem->SetAttribute("C00", matrix[0][0]); elem->SetAttribute("C01", matrix[0][1]); elem->SetAttribute("C02", matrix[0][2]); elem->SetAttribute("C03", matrix[0][3]); elem->SetAttribute("C04", matrix[0][4]); elem->SetAttribute("C05", matrix[0][5]); elem->SetAttribute("C10", matrix[1][0]); elem->SetAttribute("C11", matrix[1][1]); elem->SetAttribute("C12", matrix[1][2]); elem->SetAttribute("C13", matrix[1][3]); elem->SetAttribute("C14", matrix[1][4]); elem->SetAttribute("C15", matrix[1][5]); if (dataValid) elem->SetAttribute("Valid",1); else elem->SetAttribute("Valid",0); if (hasOrientation) elem->SetAttribute("hO",1); else elem->SetAttribute("hO",0); if (hasPosition) elem->SetAttribute("hP",1); else elem->SetAttribute("hP",0); } void mitk::Transform::FromXML(const tinyxml2::XMLElement* elem) { assert(elem); 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(); const char* typeC = elem->Attribute("Type"); std::string type = nullptr == typeC ? Transform::UNKNOWN_TYPE : typeC; elem->QueryDoubleAttribute("Time",×tamp); // position and orientation is mandatory! if(elem->QueryDoubleAttribute("X", &position[0]) != tinyxml2::XML_SUCCESS) throw std::invalid_argument("No X position found in xml"); if(elem->QueryDoubleAttribute("Y", &position[1]) != tinyxml2::XML_SUCCESS) throw std::invalid_argument("No Y position found in xml"); if(elem->QueryDoubleAttribute("Z", &position[2]) != tinyxml2::XML_SUCCESS) throw std::invalid_argument("No Z position found in xml"); if(elem->QueryDoubleAttribute("QX", &orientation[0]) != tinyxml2::XML_SUCCESS) throw std::invalid_argument("No QX orientation found in xml"); if(elem->QueryDoubleAttribute("QY", &orientation[1]) != tinyxml2::XML_SUCCESS) throw std::invalid_argument("No QY orientation found in xml"); if(elem->QueryDoubleAttribute("QZ", &orientation[2]) != tinyxml2::XML_SUCCESS) throw std::invalid_argument("No QZ orientation found in xml"); if(elem->QueryDoubleAttribute("QR", &orientation[3]) != tinyxml2::XML_SUCCESS) throw std::invalid_argument("No QR orientation found in xml"); 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); m_NavData = nd; m_Type = type; this->Modified(); } } // namespace mitk std::ostream& operator<< (std::ostream& os, mitk::Transform::Pointer p) { os << p->ToString(); return os; } diff --git a/Modules/Classification/CLMiniApps/CMakeLists.txt b/Modules/Classification/CLMiniApps/CMakeLists.txt index fb5bb3b6c8..7325e45a38 100644 --- a/Modules/Classification/CLMiniApps/CMakeLists.txt +++ b/Modules/Classification/CLMiniApps/CMakeLists.txt @@ -1,117 +1,117 @@ option(BUILD_ClassificationMiniApps "Build commandline tools for classification" OFF) if(BUILD_ClassificationMiniApps OR MITK_BUILD_ALL_APPS) 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( classificationminiapps RandomForestTraining^^MitkCLVigraRandomForest NativeHeadCTSegmentation^^MitkCLVigraRandomForest ManualSegmentationEvaluation^^MitkCLVigraRandomForest CLScreenshot^^MitkCore_MitkQtWidgetsExt_MitkCLUtilities CLDicom2Nrrd^^MitkCore CLResampleImageToReference^^MitkCore CLGlobalImageFeatures^^MitkCLUtilities_MitkQtWidgetsExt CLMRNormalization^^MitkCLUtilities_MitkCLMRUtilities CLStaple^^MitkCLUtilities CLVoxelFeatures^^MitkCLUtilities CLPolyToNrrd^^ CLPlanarFigureToNrrd^^MitkCore_MitkSegmentation_MitkMultilabel CLSimpleVoxelClassification^^MitkDataCollection_MitkCLVigraRandomForest CLVoxelClassification^^MitkDataCollection_MitkCLImportanceWeighting_MitkCLVigraRandomForest CLBrainMask^^MitkCLUtilities XRaxSimulationFromCT^^MitkCLUtilities CLRandomSampling^^MitkCore_MitkCLUtilities CLRemoveEmptyVoxels^^MitkCore CLN4^^MitkCore CLSkullMask^^MitkCore CLPointSetToSegmentation^^ CLMultiForestPrediction^^MitkDataCollection_MitkCLVigraRandomForest CLNrrdToPoly^^MitkCore CL2Dto3DImage^^MitkCore CLWeighting^^MitkCore_MitkCLImportanceWeighting_MitkCLUtilities CLOverlayRoiCenterOfMass^^MitkCore_MitkCLUtilities_MitkQtWidgetsExt CLLungSegmentation^^MitkCore_MitkSegmentation_MitkMultilabel ) foreach(classificationminiapps ${classificationminiapps}) # extract mini app name and dependencies string(REPLACE "^^" "\\;" miniapp_info ${classificationminiapps}) 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}) mitk_create_executable(${appname} DEPENDS MitkCore MitkCLCore MitkCommandLine ${dependencies_list} - PACKAGE_DEPENDS Qt5|Core Vigra VTK|IOImage + PACKAGE_DEPENDS Qt5|Core Vigra VTK|IOImage ITK|Smoothing CPP_FILES ${appname}.cpp ) if(EXECUTABLE_IS_ENABLED) # On Linux, create a shell script to start a relocatable application if(UNIX AND NOT APPLE) install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh) endif() get_target_property(_is_bundle ${EXECUTABLE_TARGET} MACOSX_BUNDLE) if(APPLE) if(_is_bundle) set(_target_locations ${EXECUTABLE_TARGET}.app) set(${_target_locations}_qt_plugins_install_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_bundle_dest_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_qt_plugins_for_current_bundle ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_qt_conf_install_dirs ${EXECUTABLE_TARGET}.app/Contents/Resources) install(TARGETS ${EXECUTABLE_TARGET} BUNDLE DESTINATION . ) else() if(NOT MACOSX_BUNDLE_NAMES) set(_qt_conf_install_dirs bin) set(_target_locations bin/${EXECUTABLE_TARGET}) set(${_target_locations}_qt_plugins_install_dir bin) install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) else() foreach(bundle_name ${MACOSX_BUNDLE_NAMES}) list(APPEND _qt_conf_install_dirs ${bundle_name}.app/Contents/Resources) set(_current_target_location ${bundle_name}.app/Contents/MacOS/${EXECUTABLE_TARGET}) list(APPEND _target_locations ${_current_target_location}) set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS) message( " set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS) ") install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION ${bundle_name}.app/Contents/MacOS/) endforeach() endif() endif() else() set(_target_locations bin/${EXECUTABLE_TARGET}${CMAKE_EXECUTABLE_SUFFIX}) set(${_target_locations}_qt_plugins_install_dir bin) set(_qt_conf_install_dirs bin) install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) endif() endif() endforeach() mitk_create_executable(CLMatchPointReg DEPENDS MitkCore MitkCLUtilities MitkMatchPointRegistration MitkCommandLine MitkMatchPointRegistrationUI PACKAGE_DEPENDS Qt5|Core Vigra MatchPoint CPP_FILES CLMatchPointReg.cpp ) # On Linux, create a shell script to start a relocatable application if(UNIX AND NOT APPLE) install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh) endif() if(EXECUTABLE_IS_ENABLED) MITK_INSTALL_TARGETS(EXECUTABLES ${EXECUTABLE_TARGET}) endif() endif() diff --git a/Modules/Classification/CLUtilities/CMakeLists.txt b/Modules/Classification/CLUtilities/CMakeLists.txt index 0f05dd8069..577381576a 100644 --- a/Modules/Classification/CLUtilities/CMakeLists.txt +++ b/Modules/Classification/CLUtilities/CMakeLists.txt @@ -1,10 +1,10 @@ mitk_create_module( DEPENDS MitkCore MitkCLCore MitkCommandLine MitkDICOM - PACKAGE_DEPENDS PUBLIC Eigen OpenMP PRIVATE tinyxml2 VTK|FiltersStatistics + PACKAGE_DEPENDS PUBLIC Eigen OpenMP PRIVATE tinyxml2 ITK|MathematicalMorphology+Smoothing VTK|FiltersStatistics ) if(TARGET ${MODULE_TARGET}) if(BUILD_TESTING) add_subdirectory(test) endif() endif() diff --git a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx index 7057b02697..8775a8aef6 100644 --- a/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx +++ b/Modules/Classification/CLUtilities/include/itkEnhancedScalarImageToRunLengthMatrixFilter.hxx @@ -1,425 +1,411 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef __itkEnhancedScalarImageToRunLengthMatrixFilter_hxx #define __itkEnhancedScalarImageToRunLengthMatrixFilter_hxx #include "itkEnhancedScalarImageToRunLengthMatrixFilter.h" #include "itkConstNeighborhoodIterator.h" #include "itkNeighborhood.h" #include "vnl/vnl_math.h" #include "itkMacro.h" namespace itk { namespace Statistics { template EnhancedScalarImageToRunLengthMatrixFilter ::EnhancedScalarImageToRunLengthMatrixFilter() : m_NumberOfBinsPerAxis( itkGetStaticConstMacro( DefaultBinsPerAxis ) ), m_Min( NumericTraits::NonpositiveMin() ), m_Max( NumericTraits::max() ), m_MinDistance( NumericTraits::ZeroValue() ), m_MaxDistance( NumericTraits::max() ), m_InsidePixelValue( NumericTraits::OneValue() ) { this->SetNumberOfRequiredInputs( 1 ); this->SetNumberOfRequiredOutputs( 1 ); const unsigned int measurementVectorSize = 2; this->ProcessObject::SetNthOutput( 0, this->MakeOutput( 0 ) ); HistogramType *output = const_cast( this->GetOutput() ); output->SetMeasurementVectorSize( measurementVectorSize ); this->m_LowerBound.SetSize( measurementVectorSize ); this->m_UpperBound.SetSize( measurementVectorSize ); this->m_LowerBound[0] = this->m_Min; this->m_LowerBound[1] = this->m_MinDistance; this->m_UpperBound[0] = this->m_Max; this->m_UpperBound[1] = this->m_MaxDistance; } template void EnhancedScalarImageToRunLengthMatrixFilter ::SetOffset( const OffsetType offset ) { OffsetVectorPointer offsetVector = OffsetVector::New(); offsetVector->push_back( offset ); this->SetOffsets( offsetVector ); } template void EnhancedScalarImageToRunLengthMatrixFilter ::SetInput( const ImageType *image ) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput( 0, const_cast( image ) ); } template void EnhancedScalarImageToRunLengthMatrixFilter ::SetMaskImage( const ImageType *image ) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput( 1, const_cast( image ) ); } template const TImageType * EnhancedScalarImageToRunLengthMatrixFilter ::GetInput() const { if( this->GetNumberOfInputs() < 1 ) { return ITK_NULLPTR; } return static_cast( this->ProcessObject::GetInput( 0 ) ); } template const TImageType * EnhancedScalarImageToRunLengthMatrixFilter ::GetMaskImage() const { if( this->GetNumberOfInputs() < 2 ) { return ITK_NULLPTR; } return static_cast( this->ProcessObject::GetInput( 1 ) ); } template const typename EnhancedScalarImageToRunLengthMatrixFilter::HistogramType * EnhancedScalarImageToRunLengthMatrixFilter ::GetOutput() const { const HistogramType *output = static_cast( this->ProcessObject::GetOutput( 0 ) ); return output; } template typename EnhancedScalarImageToRunLengthMatrixFilter::DataObjectPointer EnhancedScalarImageToRunLengthMatrixFilter ::MakeOutput( DataObjectPointerArraySizeType itkNotUsed( idx ) ) { return HistogramType::New().GetPointer(); } template void EnhancedScalarImageToRunLengthMatrixFilter ::GenerateData() { HistogramType *output = static_cast( this->ProcessObject::GetOutput( 0 ) ); const ImageType * inputImage = this->GetInput(); // First, create an appropriate histogram with the right number of bins // and mins and maxes correct for the image type. typename HistogramType::SizeType size( output->GetMeasurementVectorSize() ); size.Fill( this->m_NumberOfBinsPerAxis ); this->m_LowerBound[0] = this->m_Min; this->m_LowerBound[1] = this->m_MinDistance; this->m_UpperBound[0] = this->m_Max; this->m_UpperBound[1] = this->m_MaxDistance; output->Initialize( size, this->m_LowerBound, this->m_UpperBound ); MeasurementVectorType run( output->GetMeasurementVectorSize() ); typename HistogramType::IndexType hIndex; // Iterate over all of those pixels and offsets, adding each // distance/intensity pair to the histogram typedef ConstNeighborhoodIterator NeighborhoodIteratorType; typename NeighborhoodIteratorType::RadiusType radius; radius.Fill( 1 ); NeighborhoodIteratorType neighborIt( radius, inputImage, inputImage->GetRequestedRegion() ); // this temp image has the same dimension for each offset // moving the allocation out of loop of offsets // while keeping FillBuffer with boolean false in each loop typedef Image BoolImageType; typename BoolImageType::Pointer alreadyVisitedImage = BoolImageType::New(); alreadyVisitedImage->CopyInformation( inputImage ); alreadyVisitedImage->SetRegions( inputImage->GetRequestedRegion() ); alreadyVisitedImage->Allocate(); typename OffsetVector::ConstIterator offsets; for( offsets = this->GetOffsets()->Begin(); offsets != this->GetOffsets()->End(); offsets++ ) { alreadyVisitedImage->FillBuffer( false ); neighborIt.GoToBegin(); OffsetType offset = offsets.Value(); this->NormalizeOffsetDirection(offset); for( neighborIt.GoToBegin(); !neighborIt.IsAtEnd(); ++neighborIt ) { const PixelType centerPixelIntensity = neighborIt.GetCenterPixel(); if (centerPixelIntensity != centerPixelIntensity) // Check for invalid values { continue; } IndexType centerIndex = neighborIt.GetIndex(); if( centerPixelIntensity < this->m_Min || centerPixelIntensity > this->m_Max || alreadyVisitedImage->GetPixel( centerIndex ) || ( this->GetMaskImage() && this->GetMaskImage()->GetPixel( centerIndex ) != this->m_InsidePixelValue ) ) { continue; // don't put a pixel in the histogram if the value // is out-of-bounds or is outside the mask. } itkDebugMacro("===> offset = " << offset << std::endl); MeasurementType centerBinMin = this->GetOutput()-> GetBinMinFromValue( 0, centerPixelIntensity ); MeasurementType centerBinMax = this->GetOutput()-> GetBinMaxFromValue( 0, centerPixelIntensity ); MeasurementType lastBinMax = this->GetOutput()-> GetDimensionMaxs( 0 )[ this->GetOutput()->GetSize( 0 ) - 1 ]; PixelType pixelIntensity( NumericTraits::ZeroValue() ); IndexType index; int steps = 0; index = centerIndex + offset; IndexType lastGoodIndex = centerIndex; bool runLengthSegmentAlreadyVisited = false; // Scan from the current pixel at index, following // the direction of offset. Run length is computed as the // length of continuous pixels whose pixel values are // in the same bin. while ( inputImage->GetRequestedRegion().IsInside(index) ) { pixelIntensity = inputImage->GetPixel(index); // For the same offset, each run length segment can // only be visited once if (alreadyVisitedImage->GetPixel( index ) ) { runLengthSegmentAlreadyVisited = true; break; } if (pixelIntensity != pixelIntensity) { break; } // Special attention paid to boundaries of bins. // For the last bin, // it is left close and right close (following the previous // gerrit patch). // For all // other bins, // the bin is left close and right open. if ( pixelIntensity >= centerBinMin && ( pixelIntensity < centerBinMax || ( pixelIntensity == centerBinMax && centerBinMax == lastBinMax ) ) && (!this->GetMaskImage() || this->GetMaskImage()->GetPixel(index) == this->m_InsidePixelValue)) { alreadyVisitedImage->SetPixel( index, true ); lastGoodIndex = index; index += offset; steps++; } else { break; } } if ( runLengthSegmentAlreadyVisited ) { MITK_INFO << "Already visited 1 " << index; continue; } IndexType lastGoodIndex2 = lastGoodIndex; index = centerIndex - offset; lastGoodIndex = centerIndex; while ( inputImage->GetRequestedRegion().IsInside(index) ) { pixelIntensity = inputImage->GetPixel(index); if (pixelIntensity != pixelIntensity) { break; } if (alreadyVisitedImage->GetPixel( index ) ) { if (pixelIntensity >= centerBinMin && (pixelIntensity < centerBinMax || (pixelIntensity == centerBinMax && centerBinMax == lastBinMax))) { runLengthSegmentAlreadyVisited = true; } break; } if ( pixelIntensity >= centerBinMin && ( pixelIntensity < centerBinMax || ( pixelIntensity == centerBinMax && centerBinMax == lastBinMax ) ) && (!this->GetMaskImage() || this->GetMaskImage()->GetPixel(index) == this->m_InsidePixelValue)) { alreadyVisitedImage->SetPixel( index, true ); lastGoodIndex = index; steps++; index -= offset; } else break; } if (runLengthSegmentAlreadyVisited) { MITK_INFO << "Already visited 2 " << index; continue; } PointType centerPoint; inputImage->TransformIndexToPhysicalPoint( centerIndex, centerPoint ); PointType point; inputImage->TransformIndexToPhysicalPoint( lastGoodIndex, point ); PointType point2; inputImage->TransformIndexToPhysicalPoint( lastGoodIndex2, point2 ); run[0] = centerPixelIntensity; run[1] = steps; //run[1] = point.EuclideanDistanceTo( point2 ); if( run[1] >= this->m_MinDistance && run[1] <= this->m_MaxDistance ) { output->GetIndex( run, hIndex ); output->IncreaseFrequencyOfIndex( hIndex, 1 ); - - itkDebugStatement(typename HistogramType::IndexType tempMeasurementIndex;) - itkDebugStatement(output->GetIndex(run,tempMeasurementIndex);) - itkDebugMacro( "centerIndex<->index: " - << static_cast( centerPixelIntensity ) - << "@"<< centerIndex - << "<->" << static_cast( pixelIntensity ) << "@" << index - <<", Bin# " << tempMeasurementIndex - << ", Measurement: (" << run[0] << ", " << run[1] << ")" - << ", Center bin [" << this->GetOutput()->GetBinMinFromValue( 0, run[0] ) - << "," << this->GetOutput()->GetBinMaxFromValue( 0, run[0] ) << "]" - << "~[" << this->GetOutput()->GetBinMinFromValue( 1, run[1] ) - << "," << this->GetOutput()->GetBinMaxFromValue( 1, run[1] ) << "]" - << std::endl ); } } } } template void EnhancedScalarImageToRunLengthMatrixFilter ::SetPixelValueMinMax( PixelType min, PixelType max ) { if( this->m_Min != min || this->m_Max != max ) { itkDebugMacro( "setting Min to " << min << "and Max to " << max ); this->m_Min = min; this->m_Max = max; this->Modified(); } } template void EnhancedScalarImageToRunLengthMatrixFilter ::SetDistanceValueMinMax( RealType min, RealType max ) { if( this->m_MinDistance != min || this->m_MaxDistance != max ) { itkDebugMacro( "setting MinDistance to " << min << "and MaxDistance to " << max ); this->m_MinDistance = min; this->m_MaxDistance = max; this->Modified(); } } template void EnhancedScalarImageToRunLengthMatrixFilter ::PrintSelf( std::ostream& os, Indent indent ) const { Superclass::PrintSelf( os,indent ); os << indent << "Offsets: " << this->GetOffsets() << std::endl; os << indent << "Min: " << this->m_Min << std::endl; os << indent << "Max: " << this->m_Max << std::endl; os << indent << "Min distance: " << this->m_MinDistance << std::endl; os << indent << "Max distance: " << this->m_MaxDistance << std::endl; os << indent << "NumberOfBinsPerAxis: " << this->m_NumberOfBinsPerAxis << std::endl; os << indent << "InsidePixelValue: " << this->m_InsidePixelValue << std::endl; } template void EnhancedScalarImageToRunLengthMatrixFilter ::NormalizeOffsetDirection(OffsetType &offset) { itkDebugMacro("old offset = " << offset << std::endl); int sign = 1; bool metLastNonZero = false; for (int i = offset.GetOffsetDimension()-1; i>=0; i--) { if (metLastNonZero) { offset[i] *= sign; } else if (offset[i] != 0) { sign = (offset[i] > 0 ) ? 1 : -1; metLastNonZero = true; offset[i] *= sign; } } itkDebugMacro("new offset = " << offset << std::endl); } } // end of namespace Statistics } // end of namespace itk #endif diff --git a/Modules/Classification/CLUtilities/include/itkLocalIntensityFilter.hxx b/Modules/Classification/CLUtilities/include/itkLocalIntensityFilter.hxx index a5f14bd8c3..ac6a61b5b6 100644 --- a/Modules/Classification/CLUtilities/include/itkLocalIntensityFilter.hxx +++ b/Modules/Classification/CLUtilities/include/itkLocalIntensityFilter.hxx @@ -1,314 +1,316 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 itkLocalIntensityFilter_cpp #define itkLocalIntensityFilter_cpp #include #include #include #include #include #include "itkImageScanlineIterator.h" #include "itkProgressReporter.h" namespace itk { template< typename TInputImage > LocalIntensityFilter< TInputImage > ::LocalIntensityFilter() :m_ThreadLocalMaximum(1), m_ThreadLocalPeakValue(1), m_ThreadGlobalPeakValue(1) { + this->DynamicMultiThreadingOff(); + // first output is a copy of the image, DataObject created by // superclass // allocate the data objects for the outputs which are // just decorators around real types for (int i = 1; i < 4; ++i) { typename RealObjectType::Pointer output = static_cast< RealObjectType * >(this->MakeOutput(i).GetPointer()); this->ProcessObject::SetNthOutput(i, output.GetPointer()); } } template< typename TInputImage > DataObject::Pointer LocalIntensityFilter< TInputImage > ::MakeOutput(DataObjectPointerArraySizeType output) { switch (output) { case 0: return TInputImage::New().GetPointer(); break; case 1: case 2: case 3: case 4: case 5: case 6: return RealObjectType::New().GetPointer(); break; default: // might as well make an image return TInputImage::New().GetPointer(); break; } } template< typename TInputImage > typename LocalIntensityFilter< TInputImage >::RealObjectType * LocalIntensityFilter< TInputImage > ::GetLocalPeakOutput() { return static_cast< RealObjectType * >(this->ProcessObject::GetOutput(1)); } template< typename TInputImage > const typename LocalIntensityFilter< TInputImage >::RealObjectType * LocalIntensityFilter< TInputImage > ::GetLocalPeakOutput() const { return static_cast< const RealObjectType * >(this->ProcessObject::GetOutput(1)); } template< typename TInputImage > typename LocalIntensityFilter< TInputImage >::RealObjectType * LocalIntensityFilter< TInputImage > ::GetGlobalPeakOutput() { return static_cast< RealObjectType * >(this->ProcessObject::GetOutput(2)); } template< typename TInputImage > const typename LocalIntensityFilter< TInputImage >::RealObjectType * LocalIntensityFilter< TInputImage > ::GetGlobalPeakOutput() const { return static_cast< const RealObjectType * >(this->ProcessObject::GetOutput(2)); } template< typename TInputImage > typename LocalIntensityFilter< TInputImage >::RealObjectType * LocalIntensityFilter< TInputImage > ::GetLocalMaximumOutput() { return static_cast< RealObjectType * >(this->ProcessObject::GetOutput(3)); } template< typename TInputImage > const typename LocalIntensityFilter< TInputImage >::RealObjectType * LocalIntensityFilter< TInputImage > ::GetLocalMaximumOutput() const { return static_cast< const RealObjectType * >(this->ProcessObject::GetOutput(3)); } template< typename TInputImage > void LocalIntensityFilter< TInputImage > ::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); if (this->GetInput()) { InputImagePointer image = const_cast< typename Superclass::InputImageType * >(this->GetInput()); image->SetRequestedRegionToLargestPossibleRegion(); } } template< typename TInputImage > void LocalIntensityFilter< TInputImage > ::EnlargeOutputRequestedRegion(DataObject *data) { Superclass::EnlargeOutputRequestedRegion(data); data->SetRequestedRegionToLargestPossibleRegion(); } template< typename TInputImage > void LocalIntensityFilter< TInputImage > ::AllocateOutputs() { // Pass the input through as the output InputImagePointer image = const_cast< TInputImage * >(this->GetInput()); this->GraftOutput(image); // Nothing that needs to be allocated for the remaining outputs } template< typename TInputImage > void LocalIntensityFilter< TInputImage > ::BeforeThreadedGenerateData() { - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); + ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits(); // Resize the thread temporaries m_ThreadLocalMaximum.SetSize(numberOfThreads); m_ThreadLocalPeakValue.SetSize(numberOfThreads); m_ThreadGlobalPeakValue.SetSize(numberOfThreads); // Initialize the temporaries m_ThreadLocalMaximum.Fill(std::numeric_limits< RealType>::lowest()); m_ThreadLocalPeakValue.Fill(std::numeric_limits< RealType>::lowest()); m_ThreadGlobalPeakValue.Fill(std::numeric_limits< RealType>::lowest()); } template< typename TInputImage > void LocalIntensityFilter< TInputImage > ::AfterThreadedGenerateData() { ThreadIdType i; - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); + ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits(); RealType localMaximum = std::numeric_limits< RealType >::lowest(); RealType localPeakValue = std::numeric_limits< RealType >::lowest(); RealType globalPeakValue = std::numeric_limits< RealType >::lowest(); for (i = 0; i < numberOfThreads; i++) { globalPeakValue = std::max(globalPeakValue, m_ThreadGlobalPeakValue[i]); if (localMaximum == m_ThreadLocalMaximum[i]) { localPeakValue = std::max< RealType >(m_ThreadLocalPeakValue[i], localPeakValue); } else if (localMaximum < m_ThreadLocalMaximum[i]) { localMaximum = m_ThreadLocalMaximum[i]; localPeakValue = m_ThreadLocalPeakValue[i]; } } // Set the outputs this->GetLocalPeakOutput()->Set(localPeakValue); this->GetGlobalPeakOutput()->Set(globalPeakValue); this->GetLocalMaximumOutput()->Set(localMaximum); } template< typename TInputImage > void LocalIntensityFilter< TInputImage > ::ThreadedGenerateData(const RegionType & outputRegionForThread, ThreadIdType threadId) { typename TInputImage::ConstPointer itkImage = this->GetInput(); typename MaskImageType::Pointer itkMask = m_Mask; double range = m_Range; double minimumSpacing = 1; typename TInputImage::SizeType regionSize; int offset = std::ceil(range / minimumSpacing); regionSize.Fill(1); for (unsigned int i = 0; i < TInputImage::ImageDimension; ++i) { minimumSpacing = itkImage->GetSpacing()[i]; offset = std::ceil(range / minimumSpacing); regionSize[i] = offset; } itk::ConstNeighborhoodIterator iter(regionSize, itkImage, outputRegionForThread); itk::ConstNeighborhoodIterator iterMask(regionSize, itkMask, outputRegionForThread); typename TInputImage::PointType origin; typename TInputImage::PointType localPoint; itk::Index index; double tmpPeakValue; double globalPeakValue = std::numeric_limits::lowest(); double localPeakValue = std::numeric_limits::lowest(); PixelType localMaximum = std::numeric_limits::lowest(); std::vector vectorIsInRange; index = iter.GetIndex(); itkImage->TransformIndexToPhysicalPoint(index, origin); for (itk::SizeValueType i = 0; i < iter.Size(); ++i) { itkImage->TransformIndexToPhysicalPoint(iter.GetIndex(i), localPoint); double dist = origin.EuclideanDistanceTo(localPoint); vectorIsInRange.push_back((dist < range)); } int count = 0; iter.NeedToUseBoundaryConditionOff(); iterMask.NeedToUseBoundaryConditionOff(); auto imageSize = itkImage->GetLargestPossibleRegion().GetSize(); unsigned int imageDimension = itkImage->GetImageDimension(); while (!iter.IsAtEnd()) { if (iterMask.GetCenterPixel() > 0) { tmpPeakValue = 0; count = 0; for (itk::SizeValueType i = 0; i < iter.Size(); ++i) { if (vectorIsInRange[i]) { auto localIndex = iter.GetIndex(i); bool calculatePoint = true; for (unsigned int dimension = 0; dimension < imageDimension; ++dimension) { calculatePoint &= (localIndex[dimension] < static_cast(imageSize[dimension])); calculatePoint &= (0 <= localIndex[dimension]); } if (calculatePoint) { tmpPeakValue += iter.GetPixel(i); ++count; } } } tmpPeakValue /= count; globalPeakValue = std::max(tmpPeakValue, globalPeakValue); auto currentCenterPixelValue = iter.GetCenterPixel(); if (localMaximum == currentCenterPixelValue) { localPeakValue = std::max(tmpPeakValue, localPeakValue); } else if (localMaximum < currentCenterPixelValue) { localMaximum = currentCenterPixelValue; localPeakValue = tmpPeakValue; } } ++iterMask; ++iter; } m_ThreadLocalMaximum[threadId] = localMaximum; m_ThreadLocalPeakValue[threadId] = localPeakValue; m_ThreadGlobalPeakValue[threadId] = globalPeakValue; } template< typename TImage > void LocalIntensityFilter< TImage > ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Local Peak: " << this->GetLocalPeak() << std::endl; os << indent << "Global Peak: " << this->GetGlobalPeak() << std::endl; os << indent << "Local Maximum: " << this->GetLocalMaximum() << std::endl; } } // end namespace itk #endif diff --git a/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.hxx b/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.hxx index a8fabc92fe..d6f18f07e5 100644 --- a/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.hxx +++ b/Modules/Classification/CLUtilities/include/itkLocalStatisticFilter.hxx @@ -1,113 +1,114 @@ #ifndef itkLocalStatisticFilter_cpp #define itkLocalStatisticFilter_cpp #include #include #include #include #include "itkMinimumMaximumImageCalculator.h" #include template< class TInputImageType, class TOuputImageType> itk::LocalStatisticFilter::LocalStatisticFilter(): m_Size(5), m_Bins(5) { + this->DynamicMultiThreadingOff(); this->SetNumberOfRequiredOutputs(m_Bins); this->SetNumberOfRequiredInputs(0); for (int i = 0; i < m_Bins; ++i) { this->SetNthOutput( i, this->MakeOutput(i) ); } } template< class TInputImageType, class TOuputImageType> void itk::LocalStatisticFilter::BeforeThreadedGenerateData() { InputImagePointer input = this->GetInput(0); for (int i = 0; i < m_Bins; ++i) { CreateOutputImage(input, this->GetOutput(i)); } } template< class TInputImageType, class TOuputImageType> void itk::LocalStatisticFilter::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType /*threadId*/) { typedef itk::ImageRegionIterator IteratorType; typedef itk::ConstNeighborhoodIterator ConstIteratorType; typename TInputImageType::SizeType size; size.Fill(m_Size); InputImagePointer input = this->GetInput(0); if (TInputImageType::ImageDimension == 3) { size[2] = 0; } // MITK_INFO << "Creating output iterator"; std::vector iterVector; for (int i = 0; i < m_Bins; ++i) { IteratorType iter(this->GetOutput(i), outputRegionForThread); iterVector.push_back(iter); } ConstIteratorType inputIter(size, input, outputRegionForThread); while (!inputIter.IsAtEnd()) { for (int i = 0; i < m_Bins; ++i) { iterVector[i].Set(0); } double min = std::numeric_limits::max(); double max = std::numeric_limits::lowest(); double mean = 0; double std = 0; for (unsigned int i = 0; i < inputIter.Size(); ++i) { double value = inputIter.GetPixel(i); min = std::min(min, value); max = std::max(max, value); mean += value / inputIter.Size(); std += (value*value) / inputIter.Size(); } iterVector[0].Value() = min; iterVector[1].Value() = max; iterVector[2].Value() = mean; iterVector[3].Value() = std::sqrt(std - mean*mean); iterVector[4].Value() = max-min; for (int i = 0; i < m_Bins; ++i) { ++(iterVector[i]); } ++inputIter; } } template< class TInputImageType, class TOuputImageType> itk::ProcessObject::DataObjectPointer itk::LocalStatisticFilter::MakeOutput(itk::ProcessObject::DataObjectPointerArraySizeType /*idx*/) { itk::ProcessObject::DataObjectPointer output; output = ( TOuputImageType::New() ).GetPointer(); return output; } template< class TInputImageType, class TOuputImageType> void itk::LocalStatisticFilter::CreateOutputImage(InputImagePointer input, OutputImagePointer output) { output->SetRegions(input->GetLargestPossibleRegion()); output->Allocate(); } #endif //itkLocalStatisticFilter_cpp diff --git a/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp b/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp index b18a65904c..2eabf31065 100644 --- a/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp +++ b/Modules/Classification/CLUtilities/include/itkMultiHistogramFilter.cpp @@ -1,124 +1,125 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 itkMultiHistogramFilter_cpp #define itkMultiHistogramFilter_cpp #include #include #include #include #include "itkMinimumMaximumImageCalculator.h" template< class TInputImageType, class TOuputImageType> itk::MultiHistogramFilter::MultiHistogramFilter(): m_Delta(0.6), m_Offset(-3.0), m_Bins(11), m_Size(5), m_UseImageIntensityRange(false) { + this->DynamicMultiThreadingOff(); this->SetNumberOfRequiredOutputs(m_Bins); this->SetNumberOfRequiredInputs(0); for (int i = 0; i < m_Bins; ++i) { this->SetNthOutput( i, this->MakeOutput(i) ); } } template< class TInputImageType, class TOuputImageType> void itk::MultiHistogramFilter::BeforeThreadedGenerateData() { typedef itk::MinimumMaximumImageCalculator ImageCalculatorFilterType; if (m_UseImageIntensityRange) { typename ImageCalculatorFilterType::Pointer imageCalculatorFilter = ImageCalculatorFilterType::New(); imageCalculatorFilter->SetImage(this->GetInput(0)); imageCalculatorFilter->Compute(); m_Offset = imageCalculatorFilter->GetMinimum(); m_Delta = 1.0*(imageCalculatorFilter->GetMaximum() - imageCalculatorFilter->GetMinimum()) / (1.0*m_Bins); } InputImagePointer input = this->GetInput(0); for (int i = 0; i < m_Bins; ++i) { CreateOutputImage(input, this->GetOutput(i)); } } template< class TInputImageType, class TOuputImageType> void itk::MultiHistogramFilter::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType /*threadId*/) { double offset = m_Offset;// -3.0; double delta = m_Delta;// 0.6; typedef itk::ImageRegionIterator IteratorType; typedef itk::ConstNeighborhoodIterator ConstIteratorType; typename TInputImageType::SizeType size; size.Fill(m_Size); InputImagePointer input = this->GetInput(0); // MITK_INFO << "Creating output iterator"; std::vector iterVector; for (int i = 0; i < m_Bins; ++i) { IteratorType iter(this->GetOutput(i), outputRegionForThread); iterVector.push_back(iter); } ConstIteratorType inputIter(size, input, outputRegionForThread); while (!inputIter.IsAtEnd()) { for (int i = 0; i < m_Bins; ++i) { iterVector[i].Set(0); } for (unsigned int i = 0; i < inputIter.Size(); ++i) { double value = inputIter.GetPixel(i); value -= offset; value /= delta; auto pos = (int)(value); pos = std::max(0, std::min(m_Bins-1, pos)); iterVector[pos].Value() += 1;// (iterVector[pos].GetCenterPixel() + 1); } for (int i = 0; i < m_Bins; ++i) { ++(iterVector[i]); } ++inputIter; } } template< class TInputImageType, class TOuputImageType> itk::ProcessObject::DataObjectPointer itk::MultiHistogramFilter::MakeOutput(itk::ProcessObject::DataObjectPointerArraySizeType /*idx*/) { itk::ProcessObject::DataObjectPointer output; output = ( TOuputImageType::New() ).GetPointer(); return output; } template< class TInputImageType, class TOuputImageType> void itk::MultiHistogramFilter::CreateOutputImage(InputImagePointer input, OutputImagePointer output) { output->SetRegions(input->GetLargestPossibleRegion()); output->Allocate(); } #endif //itkMultiHistogramFilter_cpp diff --git a/Modules/Classification/CLUtilities/include/itkNeighborhoodFunctorImageFilter.h b/Modules/Classification/CLUtilities/include/itkNeighborhoodFunctorImageFilter.h index 6942ce74c4..229a132ebd 100644 --- a/Modules/Classification/CLUtilities/include/itkNeighborhoodFunctorImageFilter.h +++ b/Modules/Classification/CLUtilities/include/itkNeighborhoodFunctorImageFilter.h @@ -1,152 +1,153 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 itkNeighborhoodFunctorImageFilter_h #define itkNeighborhoodFunctorImageFilter_h #include "itkImageToImageFilter.h" #include "itkZeroFluxNeumannBoundaryCondition.h" #include "itkConstNeighborhoodIterator.h" #include "itkImage.h" #include #include #include "itkHistogram.h" namespace itk { template class NeighborhoodFunctorImageFilter : public ImageToImageFilter< TInputImageType, TFeatureImageType> { public: typedef NeighborhoodFunctorImageFilter Self; typedef ImageToImageFilter Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; itkNewMacro(Self); itkTypeMacro(NeighborhoodFunctorImageFilter, ImageToImageFilter); /** Extract some information from the image types. Dimensionality * of the two images is assumed to be the same. */ itkStaticConstMacro(ImageDimension, unsigned int, TFeatureImageType::ImageDimension); itkStaticConstMacro(InputImageDimension, unsigned int, TInputImageType::ImageDimension); typedef TInputImageType InputImageType; typedef typename TInputImageType::PixelType InputImagePixelType; typedef itk::Image MaskImageType; typedef typename MaskImageType::PixelType MaskImagePixelType; typedef TFeatureImageType FeatureImageType; typedef typename FeatureImageType::PixelType FeaturePixelType; typedef itk::Size SizeType; /** Typedef for generic boundary condition pointer. */ typedef ImageBoundaryCondition< InputImageType > * ImageBoundaryConditionPointerType; /** Typedef for the default boundary condition */ typedef ZeroFluxNeumannBoundaryCondition< InputImageType > DefaultBoundaryCondition; /** Superclass typedefs. */ typedef typename Superclass::OutputImageRegionType OutputImageRegionType; typedef Neighborhood< InputImagePixelType, InputImageDimension > NeighborhoodType; /** Allows a user to override the internal boundary condition. Care should be * be taken to ensure that the overriding boundary condition is a persistent * object during the time it is referenced. The overriding condition * can be of a different type than the default type as long as it is * a subclass of ImageBoundaryCondition. */ void OverrideBoundaryCondition(const ImageBoundaryConditionPointerType i) { m_BoundsCondition = i; } /** Get the boundary condition specified */ ImageBoundaryConditionPointerType GetBoundaryCondition() { return m_BoundsCondition; } void SetNeighborhoodSize(SizeType size){m_Size = size;} void SetNeighborhoodSize(unsigned int size){m_Size.Fill(size);} SizeType GetNeighborhoodSize(){return m_Size;} void SetMask(const typename MaskImageType::Pointer & ptr){m_MaskImage = ptr;} const FunctorType & GetFunctorReference() const { return m_Functor; } FunctorType & GetFunctorReference() { return m_Functor; } void SetFunctor(const FunctorType & func) { m_Functor = func; } protected: NeighborhoodFunctorImageFilter() { + this->DynamicMultiThreadingOff(); m_Size.Fill(0); m_MaskImage = nullptr; m_BoundsCondition = static_cast< ImageBoundaryConditionPointerType >( &m_DefaultBoundaryCondition ); this->SetNumberOfIndexedOutputs(FunctorType::OutputCount); } ~NeighborhoodFunctorImageFilter() override{} void BeforeThreadedGenerateData() override; void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) override; /** NeighborhoodFunctorImageFilter needs a larger input requested * region than the output requested region. As such, * NeighborhoodOperatorImageFilter needs to provide an implementation for * GenerateInputRequestedRegion() in order to inform the pipeline * execution model. * * \sa ProcessObject::GenerateInputRequestedRegion() */ void GenerateInputRequestedRegion() override; private: NeighborhoodFunctorImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented /** Pointer to a persistent boundary condition object used * for the image iterator. */ ImageBoundaryConditionPointerType m_BoundsCondition; /** Default boundary condition */ DefaultBoundaryCondition m_DefaultBoundaryCondition; /** Internal operator used to filter the image. */ FunctorType m_Functor; itk::Size m_Size; typename MaskImageType::Pointer m_MaskImage; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "../src/Features/itkNeighborhoodFunctorImageFilter.cpp" #endif #endif // itkFeatureImageFilter_h diff --git a/Modules/Classification/CLUtilities/src/Features/itkLineHistogramBasedMassImageFilter.cpp b/Modules/Classification/CLUtilities/src/Features/itkLineHistogramBasedMassImageFilter.cpp index ad2cb43f2c..626150db4b 100644 --- a/Modules/Classification/CLUtilities/src/Features/itkLineHistogramBasedMassImageFilter.cpp +++ b/Modules/Classification/CLUtilities/src/Features/itkLineHistogramBasedMassImageFilter.cpp @@ -1,254 +1,256 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 ITKLINEHISTOGRAMBASEDMASSIMAGEFILTER_CPP #define ITKLINEHISTOGRAMBASEDMASSIMAGEFILTER_CPP #include +#include #include #include #include #include template< class TInputImageType, class TOutputImageType, class TMaskImageType> void itk::LineHistogramBasedMassImageFilter ::BeforeThreadedGenerateData() { if(m_ImageMask.IsNull()) { itkExceptionMacro("No binary mask image provided.") } if(m_BinaryContour.IsNull()){ typename TMaskImageType::RegionType region = m_ImageMask->GetLargestPossibleRegion(); unsigned int xdim = region.GetSize(0); unsigned int ydim = region.GetSize(1); unsigned int zdim = region.GetSize(2); // ensure border pixels are zero so that an closed contour is created typename TMaskImageType::Pointer mask_copy = TMaskImageType::New(); mask_copy->SetSpacing(m_ImageMask->GetSpacing()); mask_copy->SetDirection(m_ImageMask->GetDirection()); mask_copy->SetOrigin(m_ImageMask->GetOrigin()); mask_copy->SetRegions(region); mask_copy->Allocate(); mask_copy->FillBuffer(0); itk::ImageRegionIteratorWithIndex oit(mask_copy, mask_copy->GetLargestPossibleRegion()); itk::ImageRegionConstIteratorWithIndex mit(m_ImageMask, m_ImageMask->GetLargestPossibleRegion()); while(!mit.IsAtEnd()) { if(mit.Value() != 0 //is under the mask && mit.GetIndex()[0] != 0 //is not at the border && mit.GetIndex()[1] != 0 && mit.GetIndex()[2] != 0 && mit.GetIndex()[0] != xdim-1 && mit.GetIndex()[1] != ydim-1 && mit.GetIndex()[2] != zdim-1) { oit.Set(1); } ++mit; ++oit; } typedef itk::BinaryContourImageFilter BinaryContourImagefilterType; typename BinaryContourImagefilterType::Pointer filter = BinaryContourImagefilterType::New(); filter->SetInput(mask_copy); filter->SetBackgroundValue(0); filter->SetForegroundValue(1); filter->SetFullyConnected(true); filter->Update(); m_BinaryContour = filter->GetOutput(); mitk::Image::Pointer outimg; mitk::CastToMitkImage(m_BinaryContour,outimg); } if(m_BinaryContour.IsNull()) { itkExceptionMacro("No binary contour image provided.") } m_CenterOfMask = GetCenterOfMass(m_ImageMask); if(m_CenterOfMask.is_zero()) { itkExceptionMacro("Center of mass is corrupt.") } } template< class TInputImageType, class TOutputImageType, class TMaskImageType> vnl_vector itk::LineHistogramBasedMassImageFilter ::GetCenterOfMass(const TMaskImageType * maskImage) { mitk::Image::Pointer img; mitk::CastToMitkImage(maskImage, img); typedef itk::ImageRegionConstIterator< TMaskImageType > IteratorType; IteratorType iter( maskImage, maskImage->GetLargestPossibleRegion() ); iter.GoToBegin(); vnl_vector_fixed mean_index; mean_index.fill(0); unsigned int count = 0; while ( !iter.IsAtEnd() ) { if ( iter.Get() != 0 ) { mitk::Point3D current_index_pos; img->GetGeometry()->IndexToWorld(iter.GetIndex(),current_index_pos); mean_index += current_index_pos.GetVnlVector(); count++; } ++iter; } mean_index /= count; - return mean_index; + return mean_index.as_ref(); } template< class TInputImageType, class TOutputImageType, class TMaskImageType> void itk::LineHistogramBasedMassImageFilter ::ThreadedGenerateData(const typename Superclass::OutputImageRegionType &outputRegionForThread, ThreadIdType /*threadId*/) { TOutputImageType * outimage = this->GetOutput(); itk::ImageRegionConstIteratorWithIndex cit(m_BinaryContour, outputRegionForThread); itk::ImageRegionConstIteratorWithIndex iit(this->GetInput(), outputRegionForThread); itk::ImageRegionIteratorWithIndex oit(outimage,outputRegionForThread); typedef typename itk::ImageRegionIteratorWithIndex::IndexType IndexType; std::vector target_world_indices; while(!cit.IsAtEnd()) { if(cit.Value() != 0 ) target_world_indices.push_back(cit.GetIndex()); ++cit; } mitk::Image::Pointer image; mitk::CastToMitkImage(this->GetInput(), image); mitk::BaseGeometry * transform = image->GetGeometry(); while(!target_world_indices.empty()) { vnl_vector u_v(3,1), x_v(3,1), p_v(3,1); double line_histo_area = 0; // w->i skull point mitk::Point3D skull_point, tmp_point; image->GetGeometry()->IndexToWorld(target_world_indices.back(),skull_point); // project centerpoint to slice // x = p + s*u u_v = skull_point.GetVnlVector() - m_CenterOfMask; u_v.normalize(); p_v = m_CenterOfMask; //step width double s_s = 0.1; // i->w center point mitk::Point3D center_point; center_point[0] = m_CenterOfMask[0]; center_point[1] = m_CenterOfMask[1]; center_point[2] = m_CenterOfMask[2]; IndexType tmp_index; transform->WorldToIndex(center_point,tmp_index); oit.SetIndex(tmp_index); std::vector under_line_indices; while(true) { // store current_index under_line_indices.push_back(tmp_index); // set histo value iit.SetIndex(tmp_index); line_histo_area += iit.Value(); // break in end reached if(tmp_index == target_world_indices.back()) { break; } // get next voxel index under the line while(tmp_index == oit.GetIndex()) // if new voxel index reached brake { x_v = p_v + s_s * u_v; // next tmp_point.GetVnlVector().set(x_v.data_block()); transform->WorldToIndex(tmp_point, tmp_index); s_s+=0.1; } oit.SetIndex(tmp_index); } while (!under_line_indices.empty()) { IndexType current_index = under_line_indices.back(); under_line_indices.pop_back(); oit.SetIndex(current_index); oit.Set(line_histo_area / (s_s * u_v).magnitude() ); } target_world_indices.pop_back(); } } template< class TInputImageType, class TOutputImageType, class TMaskImageType> void itk::LineHistogramBasedMassImageFilter::SetImageMask(TMaskImageType * maskimage) { this->m_ImageMask = maskimage; } template< class TInputImageType, class TOutputImageType, class TMaskImageType> void itk::LineHistogramBasedMassImageFilter::SetBinaryContour(TMaskImageType * binarycontour) { this->m_BinaryContour = binarycontour; } template< class TInputImageType, class TOutputImageType, class TMaskImageType> itk::LineHistogramBasedMassImageFilter::LineHistogramBasedMassImageFilter() { + this->DynamicMultiThreadingOff(); this->SetNumberOfIndexedOutputs(1); this->SetNumberOfIndexedInputs(1); m_CenterOfMask.fill(0); } template< class TInputImageType, class TOutputImageType, class TMaskImageType> itk::LineHistogramBasedMassImageFilter::~LineHistogramBasedMassImageFilter() { } #endif diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkPURFClassifier.h b/Modules/Classification/CLVigraRandomForest/include/mitkPURFClassifier.h index 4eadac9ea3..71e3397d06 100644 --- a/Modules/Classification/CLVigraRandomForest/include/mitkPURFClassifier.h +++ b/Modules/Classification/CLVigraRandomForest/include/mitkPURFClassifier.h @@ -1,93 +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. ============================================================================*/ #ifndef mitkPURFClassifier_h #define mitkPURFClassifier_h #include #include //#include #include #include namespace mitk { class MITKCLVIGRARANDOMFOREST_EXPORT PURFClassifier : public AbstractClassifier { public: mitkClassMacro(PURFClassifier, AbstractClassifier); itkFactorylessNewMacro(Self); itkCloneMacro(Self); PURFClassifier(); ~PURFClassifier() override; void Train(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y) override; Eigen::MatrixXi Predict(const Eigen::MatrixXd &X) override; Eigen::MatrixXi PredictWeighted(const Eigen::MatrixXd &X); bool SupportsPointWiseWeight() override; bool SupportsPointWiseProbability() override; void ConvertParameter(); vigra::ArrayVector CalculateKappa(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in); void SetRandomForest(const vigra::RandomForest & rf); const vigra::RandomForest & GetRandomForest() const; void UsePointWiseWeight(bool) override; void SetMaximumTreeDepth(int); void SetMinimumSplitNodeSize(int); void SetPrecision(double); void SetSamplesPerTree(double); void UseSampleWithReplacement(bool); void SetTreeCount(int); void SetWeightLambda(double); void PrintParameter(std::ostream &str = std::cout); void SetClassProbabilities(Eigen::VectorXd probabilities); Eigen::VectorXd GetClassProbabilites(); private: // *------------------- // * THREADING // *------------------- struct TrainingData; struct PredictionData; struct EigenToVigraTransform; struct Parameter; vigra::MultiArrayView<2, double> m_Probabilities; Eigen::MatrixXd m_TreeWeights; Eigen::VectorXd m_ClassProbabilities; Parameter * m_Parameter; vigra::RandomForest m_RandomForest; - static ITK_THREAD_RETURN_TYPE TrainTreesCallback(void *); - static ITK_THREAD_RETURN_TYPE PredictCallback(void *); - static ITK_THREAD_RETURN_TYPE PredictWeightedCallback(void *); + static itk::ITK_THREAD_RETURN_TYPE TrainTreesCallback(void *); + static itk::ITK_THREAD_RETURN_TYPE PredictCallback(void *); + static itk::ITK_THREAD_RETURN_TYPE PredictWeightedCallback(void *); static void VigraPredictWeighted(PredictionData *data, vigra::MultiArrayView<2, double> & X, vigra::MultiArrayView<2, int> & Y, vigra::MultiArrayView<2, double> & P); }; } #endif //mitkPURFClassifier_h diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h b/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h index b167df6c1e..13df06b8bd 100644 --- a/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h +++ b/Modules/Classification/CLVigraRandomForest/include/mitkVigraRandomForestClassifier.h @@ -1,92 +1,92 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkVigraRandomForestClassifier_h #define mitkVigraRandomForestClassifier_h #include #include //#include #include #include namespace mitk { class MITKCLVIGRARANDOMFOREST_EXPORT VigraRandomForestClassifier : public AbstractClassifier { public: mitkClassMacro(VigraRandomForestClassifier, AbstractClassifier); itkFactorylessNewMacro(Self); itkCloneMacro(Self); VigraRandomForestClassifier(); ~VigraRandomForestClassifier() override; void Train(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y) override; void OnlineTrain(const Eigen::MatrixXd &X, const Eigen::MatrixXi &Y); Eigen::MatrixXi Predict(const Eigen::MatrixXd &X) override; Eigen::MatrixXi PredictWeighted(const Eigen::MatrixXd &X); bool SupportsPointWiseWeight() override; bool SupportsPointWiseProbability() override; void ConvertParameter(); void SetRandomForest(const vigra::RandomForest & rf); const vigra::RandomForest & GetRandomForest() const; void UsePointWiseWeight(bool) override; void SetMaximumTreeDepth(int); void SetMinimumSplitNodeSize(int); void SetPrecision(double); void SetSamplesPerTree(double); void UseSampleWithReplacement(bool); void SetTreeCount(int); void SetWeightLambda(double); void SetTreeWeights(Eigen::MatrixXd weights); void SetTreeWeight(int treeId, double weight); Eigen::MatrixXd GetTreeWeights() const; void PrintParameter(std::ostream &str = std::cout); private: // *------------------- // * THREADING // *------------------- struct TrainingData; struct PredictionData; struct EigenToVigraTransform; struct Parameter; vigra::MultiArrayView<2, double> m_Probabilities; Eigen::MatrixXd m_TreeWeights; Parameter * m_Parameter; vigra::RandomForest m_RandomForest; - static ITK_THREAD_RETURN_TYPE TrainTreesCallback(void *); - static ITK_THREAD_RETURN_TYPE PredictCallback(void *); - static ITK_THREAD_RETURN_TYPE PredictWeightedCallback(void *); + static itk::ITK_THREAD_RETURN_TYPE TrainTreesCallback(void *); + static itk::ITK_THREAD_RETURN_TYPE PredictCallback(void *); + static itk::ITK_THREAD_RETURN_TYPE PredictWeightedCallback(void *); static void VigraPredictWeighted(PredictionData *data, vigra::MultiArrayView<2, double> & X, vigra::MultiArrayView<2, int> & Y, vigra::MultiArrayView<2, double> & P); }; } #endif //mitkVigraRandomForestClassifier_h diff --git a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkPURFClassifier.cpp b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkPURFClassifier.cpp index f686e14da4..05f8414cc7 100644 --- a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkPURFClassifier.cpp +++ b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkPURFClassifier.cpp @@ -1,474 +1,474 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 includes #include #include #include #include #include #include // Vigra includes #include #include // ITK include -#include -#include +#include #include +#include + typedef mitk::ThresholdSplit >,int,vigra::ClassificationTag> DefaultPUSplitType; struct mitk::PURFClassifier::Parameter { vigra::RF_OptionTag Stratification; bool SampleWithReplacement; bool UseRandomSplit; bool UsePointBasedWeights; int TreeCount; int MinimumSplitNodeSize; int TreeDepth; double Precision; double WeightLambda; double SamplesPerTree; }; struct mitk::PURFClassifier::TrainingData { TrainingData(unsigned int numberOfTrees, const vigra::RandomForest & refRF, const DefaultPUSplitType & refSplitter, const vigra::MultiArrayView<2, double> refFeature, const vigra::MultiArrayView<2, int> refLabel, const Parameter parameter) : m_ClassCount(0), m_NumberOfTrees(numberOfTrees), m_RandomForest(refRF), m_Splitter(refSplitter), m_Feature(refFeature), m_Label(refLabel), m_Parameter(parameter) { - m_mutex = itk::FastMutexLock::New(); } vigra::ArrayVector::DecisionTree_t> trees_; int m_ClassCount; unsigned int m_NumberOfTrees; const vigra::RandomForest & m_RandomForest; const DefaultPUSplitType & m_Splitter; const vigra::MultiArrayView<2, double> m_Feature; const vigra::MultiArrayView<2, int> m_Label; - itk::FastMutexLock::Pointer m_mutex; + std::mutex m_mutex; Parameter m_Parameter; }; struct mitk::PURFClassifier::PredictionData { PredictionData(const vigra::RandomForest & refRF, const vigra::MultiArrayView<2, double> refFeature, vigra::MultiArrayView<2, int> refLabel, vigra::MultiArrayView<2, double> refProb, vigra::MultiArrayView<2, double> refTreeWeights) : m_RandomForest(refRF), m_Feature(refFeature), m_Label(refLabel), m_Probabilities(refProb), m_TreeWeights(refTreeWeights) { } const vigra::RandomForest & m_RandomForest; const vigra::MultiArrayView<2, double> m_Feature; vigra::MultiArrayView<2, int> m_Label; vigra::MultiArrayView<2, double> m_Probabilities; vigra::MultiArrayView<2, double> m_TreeWeights; }; mitk::PURFClassifier::PURFClassifier() :m_Parameter(nullptr) { itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &mitk::PURFClassifier::ConvertParameter); this->GetPropertyList()->AddObserver( itk::ModifiedEvent(), command ); } mitk::PURFClassifier::~PURFClassifier() { } void mitk::PURFClassifier::SetClassProbabilities(Eigen::VectorXd probabilities) { m_ClassProbabilities = probabilities; } Eigen::VectorXd mitk::PURFClassifier::GetClassProbabilites() { return m_ClassProbabilities; } bool mitk::PURFClassifier::SupportsPointWiseWeight() { return true; } bool mitk::PURFClassifier::SupportsPointWiseProbability() { return true; } vigra::ArrayVector mitk::PURFClassifier::CalculateKappa(const Eigen::MatrixXd & /* X_in */, const Eigen::MatrixXi & Y_in) { int maximumValue = Y_in.maxCoeff(); vigra::ArrayVector kappa(maximumValue + 1); vigra::ArrayVector counts(maximumValue + 1); for (int i = 0; i < Y_in.rows(); ++i) { counts[Y_in(i, 0)] += 1; } for (int i = 0; i < maximumValue+1; ++i) { if (counts[i] > 0) { kappa[i] = counts[0] * m_ClassProbabilities[i] / counts[i] + 1; } else { kappa[i] = 1; } } return kappa; } void mitk::PURFClassifier::Train(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in) { this->ConvertParameter(); PURFData* purfData = new PURFData; purfData->m_Kappa = this->CalculateKappa(X_in, Y_in); DefaultPUSplitType splitter; splitter.UsePointBasedWeights(m_Parameter->UsePointBasedWeights); splitter.UseRandomSplit(m_Parameter->UseRandomSplit); splitter.SetPrecision(m_Parameter->Precision); splitter.SetMaximumTreeDepth(m_Parameter->TreeDepth); splitter.SetAdditionalData(purfData); // Weights handled as member variable if (m_Parameter->UsePointBasedWeights) { // Set influence of the weight (0 no influenc to 1 max influence) this->m_PointWiseWeight.unaryExpr([this](double t){ return std::pow(t, this->m_Parameter->WeightLambda) ;}); vigra::MultiArrayView<2, double> W(vigra::Shape2(this->m_PointWiseWeight.rows(),this->m_PointWiseWeight.cols()),this->m_PointWiseWeight.data()); splitter.SetWeights(W); } vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(Y_in.rows(),Y_in.cols()),Y_in.data()); m_RandomForest.set_options().tree_count(1); // Number of trees that are calculated; m_RandomForest.set_options().use_stratification(m_Parameter->Stratification); m_RandomForest.set_options().sample_with_replacement(m_Parameter->SampleWithReplacement); m_RandomForest.set_options().samples_per_tree(m_Parameter->SamplesPerTree); m_RandomForest.set_options().min_split_node_size(m_Parameter->MinimumSplitNodeSize); m_RandomForest.learn(X, Y,vigra::rf::visitors::VisitorBase(),splitter); std::unique_ptr data(new TrainingData(m_Parameter->TreeCount,m_RandomForest,splitter,X,Y, *m_Parameter)); - itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); + auto threader = itk::MultiThreaderBase::New(); threader->SetSingleMethod(this->TrainTreesCallback,data.get()); threader->SingleMethodExecute(); // set result trees m_RandomForest.set_options().tree_count(m_Parameter->TreeCount); m_RandomForest.ext_param_.class_count_ = data->m_ClassCount; m_RandomForest.trees_ = data->trees_; // Set Tree Weights to default m_TreeWeights = Eigen::MatrixXd(m_Parameter->TreeCount,1); m_TreeWeights.fill(1.0); delete purfData; } Eigen::MatrixXi mitk::PURFClassifier::Predict(const Eigen::MatrixXd &X_in) { // Initialize output Eigen matrices m_OutProbability = Eigen::MatrixXd(X_in.rows(),m_RandomForest.class_count()); m_OutProbability.fill(0); m_OutLabel = Eigen::MatrixXi(X_in.rows(),1); m_OutLabel.fill(0); // If no weights provided if(m_TreeWeights.rows() != m_RandomForest.tree_count()) { m_TreeWeights = Eigen::MatrixXd(m_RandomForest.tree_count(),1); m_TreeWeights.fill(1); } vigra::MultiArrayView<2, double> P(vigra::Shape2(m_OutProbability.rows(),m_OutProbability.cols()),m_OutProbability.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(m_OutLabel.rows(),m_OutLabel.cols()),m_OutLabel.data()); vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, double> TW(vigra::Shape2(m_RandomForest.tree_count(),1),m_TreeWeights.data()); std::unique_ptr data; data.reset(new PredictionData(m_RandomForest, X, Y, P, TW)); - itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); + auto threader = itk::MultiThreaderBase::New(); threader->SetSingleMethod(this->PredictCallback, data.get()); threader->SingleMethodExecute(); m_Probabilities = data->m_Probabilities; return m_OutLabel; } -ITK_THREAD_RETURN_TYPE mitk::PURFClassifier::TrainTreesCallback(void * arg) +itk::ITK_THREAD_RETURN_TYPE mitk::PURFClassifier::TrainTreesCallback(void * arg) { // Get the ThreadInfoStruct - typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType; + typedef itk::MultiThreaderBase::WorkUnitInfo ThreadInfoType; ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg ); TrainingData * data = (TrainingData *)(infoStruct->UserData); unsigned int numberOfTreesToCalculate = 0; // define the number of tress the forest have to calculate - numberOfTreesToCalculate = data->m_NumberOfTrees / infoStruct->NumberOfThreads; + numberOfTreesToCalculate = data->m_NumberOfTrees / infoStruct->NumberOfWorkUnits; // the 0th thread takes the residuals - if(infoStruct->ThreadID == 0) numberOfTreesToCalculate += data->m_NumberOfTrees % infoStruct->NumberOfThreads; + if(infoStruct->WorkUnitID == 0) numberOfTreesToCalculate += data->m_NumberOfTrees % infoStruct->NumberOfWorkUnits; if(numberOfTreesToCalculate != 0){ // Copy the Treestructure defined in userData vigra::RandomForest rf = data->m_RandomForest; // Initialize a splitter for the leraning process DefaultPUSplitType splitter; splitter.UsePointBasedWeights(data->m_Splitter.IsUsingPointBasedWeights()); splitter.UseRandomSplit(data->m_Splitter.IsUsingRandomSplit()); splitter.SetPrecision(data->m_Splitter.GetPrecision()); splitter.SetMaximumTreeDepth(data->m_Splitter.GetMaximumTreeDepth()); splitter.SetWeights(data->m_Splitter.GetWeights()); splitter.SetAdditionalData(data->m_Splitter.GetAdditionalData()); rf.trees_.clear(); rf.set_options().tree_count(numberOfTreesToCalculate); rf.set_options().use_stratification(data->m_Parameter.Stratification); rf.set_options().sample_with_replacement(data->m_Parameter.SampleWithReplacement); rf.set_options().samples_per_tree(data->m_Parameter.SamplesPerTree); rf.set_options().min_split_node_size(data->m_Parameter.MinimumSplitNodeSize); rf.learn(data->m_Feature, data->m_Label,vigra::rf::visitors::VisitorBase(),splitter); - data->m_mutex->Lock(); + data->m_mutex.lock(); for(const auto & tree : rf.trees_) data->trees_.push_back(tree); data->m_ClassCount = rf.class_count(); - data->m_mutex->Unlock(); + data->m_mutex.unlock(); } - return ITK_THREAD_RETURN_VALUE; + return ITK_THREAD_RETURN_DEFAULT_VALUE; } -ITK_THREAD_RETURN_TYPE mitk::PURFClassifier::PredictCallback(void * arg) +itk::ITK_THREAD_RETURN_TYPE mitk::PURFClassifier::PredictCallback(void * arg) { // Get the ThreadInfoStruct - typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType; + typedef itk::MultiThreaderBase::WorkUnitInfo ThreadInfoType; ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg ); // assigne the thread id - const unsigned int threadId = infoStruct->ThreadID; + const unsigned int threadId = infoStruct->WorkUnitID; // Get the user defined parameters containing all // neccesary informations PredictionData * data = (PredictionData *)(infoStruct->UserData); unsigned int numberOfRowsToCalculate = 0; // Get number of rows to calculate - numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfThreads; + numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfWorkUnits; unsigned int start_index = numberOfRowsToCalculate * threadId; unsigned int end_index = numberOfRowsToCalculate * (threadId+1); // the last thread takes the residuals - if(threadId == infoStruct->NumberOfThreads-1) { - end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfThreads; + if(threadId == infoStruct->NumberOfWorkUnits-1) { + end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfWorkUnits; } vigra::MultiArrayView<2, double> split_features; vigra::MultiArrayView<2, int> split_labels; vigra::MultiArrayView<2, double> split_probability; { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Feature.shape(1)); split_features = data->m_Feature.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index, data->m_Label.shape(1)); split_labels = data->m_Label.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Probabilities.shape(1)); split_probability = data->m_Probabilities.subarray(lowerBound,upperBound); } data->m_RandomForest.predictLabels(split_features,split_labels); data->m_RandomForest.predictProbabilities(split_features, split_probability); - return ITK_THREAD_RETURN_VALUE; + return ITK_THREAD_RETURN_DEFAULT_VALUE; } void mitk::PURFClassifier::ConvertParameter() { if(this->m_Parameter == nullptr) this->m_Parameter = new Parameter(); // Get the proerty // Some defaults MITK_INFO("PURFClassifier") << "Convert Parameter"; if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights)) this->m_Parameter->UsePointBasedWeights = false; if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit)) this->m_Parameter->UseRandomSplit = false; if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth)) this->m_Parameter->TreeDepth = 20; if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount)) this->m_Parameter->TreeCount = 100; if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize)) this->m_Parameter->MinimumSplitNodeSize = 5; if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision)) this->m_Parameter->Precision = mitk::eps; if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree)) this->m_Parameter->SamplesPerTree = 0.6; if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement)) this->m_Parameter->SampleWithReplacement = true; if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda)) this->m_Parameter->WeightLambda = 1.0; // Not used yet // if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification)) this->m_Parameter->Stratification = vigra::RF_NONE; // no Property given } void mitk::PURFClassifier::PrintParameter(std::ostream & str) { if(this->m_Parameter == nullptr) { MITK_WARN("PURFClassifier") << "Parameters are not initialized. Please call ConvertParameter() first!"; return; } this->ConvertParameter(); // Get the proerty // Some defaults if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights)) str << "usepointbasedweight\tNOT SET (default " << this->m_Parameter->UsePointBasedWeights << ")" << "\n"; else str << "usepointbasedweight\t" << this->m_Parameter->UsePointBasedWeights << "\n"; if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit)) str << "userandomsplit\tNOT SET (default " << this->m_Parameter->UseRandomSplit << ")" << "\n"; else str << "userandomsplit\t" << this->m_Parameter->UseRandomSplit << "\n"; if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth)) str << "treedepth\t\tNOT SET (default " << this->m_Parameter->TreeDepth << ")" << "\n"; else str << "treedepth\t\t" << this->m_Parameter->TreeDepth << "\n"; if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize)) str << "minimalsplitnodesize\tNOT SET (default " << this->m_Parameter->MinimumSplitNodeSize << ")" << "\n"; else str << "minimalsplitnodesize\t" << this->m_Parameter->MinimumSplitNodeSize << "\n"; if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision)) str << "precision\t\tNOT SET (default " << this->m_Parameter->Precision << ")" << "\n"; else str << "precision\t\t" << this->m_Parameter->Precision << "\n"; if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree)) str << "samplespertree\tNOT SET (default " << this->m_Parameter->SamplesPerTree << ")" << "\n"; else str << "samplespertree\t" << this->m_Parameter->SamplesPerTree << "\n"; if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement)) str << "samplewithreplacement\tNOT SET (default " << this->m_Parameter->SampleWithReplacement << ")" << "\n"; else str << "samplewithreplacement\t" << this->m_Parameter->SampleWithReplacement << "\n"; if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount)) str << "treecount\t\tNOT SET (default " << this->m_Parameter->TreeCount << ")" << "\n"; else str << "treecount\t\t" << this->m_Parameter->TreeCount << "\n"; if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda)) str << "lambda\t\tNOT SET (default " << this->m_Parameter->WeightLambda << ")" << "\n"; else str << "lambda\t\t" << this->m_Parameter->WeightLambda << "\n"; // if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification)) // this->m_Parameter->Stratification = vigra:RF_NONE; // no Property given } void mitk::PURFClassifier::UsePointWiseWeight(bool val) { mitk::AbstractClassifier::UsePointWiseWeight(val); this->GetPropertyList()->SetBoolProperty("usepointbasedweight",val); } void mitk::PURFClassifier::SetMaximumTreeDepth(int val) { this->GetPropertyList()->SetIntProperty("treedepth",val); } void mitk::PURFClassifier::SetMinimumSplitNodeSize(int val) { this->GetPropertyList()->SetIntProperty("minimalsplitnodesize",val); } void mitk::PURFClassifier::SetPrecision(double val) { this->GetPropertyList()->SetDoubleProperty("precision",val); } void mitk::PURFClassifier::SetSamplesPerTree(double val) { this->GetPropertyList()->SetDoubleProperty("samplespertree",val); } void mitk::PURFClassifier::UseSampleWithReplacement(bool val) { this->GetPropertyList()->SetBoolProperty("samplewithreplacement",val); } void mitk::PURFClassifier::SetTreeCount(int val) { this->GetPropertyList()->SetIntProperty("treecount",val); } void mitk::PURFClassifier::SetWeightLambda(double val) { this->GetPropertyList()->SetDoubleProperty("lambda",val); } void mitk::PURFClassifier::SetRandomForest(const vigra::RandomForest & rf) { this->SetMaximumTreeDepth(rf.ext_param().max_tree_depth); this->SetMinimumSplitNodeSize(rf.options().min_split_node_size_); this->SetTreeCount(rf.options().tree_count_); this->SetSamplesPerTree(rf.options().training_set_proportion_); this->UseSampleWithReplacement(rf.options().sample_with_replacement_); this->m_RandomForest = rf; } const vigra::RandomForest & mitk::PURFClassifier::GetRandomForest() const { return this->m_RandomForest; } diff --git a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp index 95d0295d2e..840d58d270 100644 --- a/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp +++ b/Modules/Classification/CLVigraRandomForest/src/Classifier/mitkVigraRandomForestClassifier.cpp @@ -1,589 +1,589 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 includes #include #include #include #include #include // Vigra includes #include #include // ITK include -#include -#include +#include #include +#include + typedef mitk::ThresholdSplit >,int,vigra::ClassificationTag> DefaultSplitType; struct mitk::VigraRandomForestClassifier::Parameter { vigra::RF_OptionTag Stratification; bool SampleWithReplacement; bool UseRandomSplit; bool UsePointBasedWeights; int TreeCount; int MinimumSplitNodeSize; int TreeDepth; double Precision; double WeightLambda; double SamplesPerTree; }; struct mitk::VigraRandomForestClassifier::TrainingData { TrainingData(unsigned int numberOfTrees, const vigra::RandomForest & refRF, const DefaultSplitType & refSplitter, const vigra::MultiArrayView<2, double> refFeature, const vigra::MultiArrayView<2, int> refLabel, const Parameter parameter) : m_ClassCount(0), m_NumberOfTrees(numberOfTrees), m_RandomForest(refRF), m_Splitter(refSplitter), m_Feature(refFeature), m_Label(refLabel), m_Parameter(parameter) { - m_mutex = itk::FastMutexLock::New(); } vigra::ArrayVector::DecisionTree_t> trees_; int m_ClassCount; unsigned int m_NumberOfTrees; const vigra::RandomForest & m_RandomForest; const DefaultSplitType & m_Splitter; const vigra::MultiArrayView<2, double> m_Feature; const vigra::MultiArrayView<2, int> m_Label; - itk::FastMutexLock::Pointer m_mutex; + std::mutex m_mutex; Parameter m_Parameter; }; struct mitk::VigraRandomForestClassifier::PredictionData { PredictionData(const vigra::RandomForest & refRF, const vigra::MultiArrayView<2, double> refFeature, vigra::MultiArrayView<2, int> refLabel, vigra::MultiArrayView<2, double> refProb, vigra::MultiArrayView<2, double> refTreeWeights) : m_RandomForest(refRF), m_Feature(refFeature), m_Label(refLabel), m_Probabilities(refProb), m_TreeWeights(refTreeWeights) { } const vigra::RandomForest & m_RandomForest; const vigra::MultiArrayView<2, double> m_Feature; vigra::MultiArrayView<2, int> m_Label; vigra::MultiArrayView<2, double> m_Probabilities; vigra::MultiArrayView<2, double> m_TreeWeights; }; mitk::VigraRandomForestClassifier::VigraRandomForestClassifier() :m_Parameter(nullptr) { itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &mitk::VigraRandomForestClassifier::ConvertParameter); this->GetPropertyList()->AddObserver( itk::ModifiedEvent(), command ); } mitk::VigraRandomForestClassifier::~VigraRandomForestClassifier() { } bool mitk::VigraRandomForestClassifier::SupportsPointWiseWeight() { return true; } bool mitk::VigraRandomForestClassifier::SupportsPointWiseProbability() { return true; } void mitk::VigraRandomForestClassifier::OnlineTrain(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in) { vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(Y_in.rows(),Y_in.cols()),Y_in.data()); m_RandomForest.onlineLearn(X,Y,0,true); } void mitk::VigraRandomForestClassifier::Train(const Eigen::MatrixXd & X_in, const Eigen::MatrixXi &Y_in) { this->ConvertParameter(); DefaultSplitType splitter; splitter.UsePointBasedWeights(m_Parameter->UsePointBasedWeights); splitter.UseRandomSplit(m_Parameter->UseRandomSplit); splitter.SetPrecision(m_Parameter->Precision); splitter.SetMaximumTreeDepth(m_Parameter->TreeDepth); // Weights handled as member variable if (m_Parameter->UsePointBasedWeights) { // Set influence of the weight (0 no influenc to 1 max influence) this->m_PointWiseWeight.unaryExpr([this](double t){ return std::pow(t, this->m_Parameter->WeightLambda) ;}); vigra::MultiArrayView<2, double> W(vigra::Shape2(this->m_PointWiseWeight.rows(),this->m_PointWiseWeight.cols()),this->m_PointWiseWeight.data()); splitter.SetWeights(W); } vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(Y_in.rows(),Y_in.cols()),Y_in.data()); m_RandomForest.set_options().tree_count(1); // Number of trees that are calculated; m_RandomForest.set_options().use_stratification(m_Parameter->Stratification); m_RandomForest.set_options().sample_with_replacement(m_Parameter->SampleWithReplacement); m_RandomForest.set_options().samples_per_tree(m_Parameter->SamplesPerTree); m_RandomForest.set_options().min_split_node_size(m_Parameter->MinimumSplitNodeSize); m_RandomForest.learn(X, Y,vigra::rf::visitors::VisitorBase(),splitter); std::unique_ptr data(new TrainingData(m_Parameter->TreeCount,m_RandomForest,splitter,X,Y, *m_Parameter)); - itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); + auto threader = itk::MultiThreaderBase::New(); threader->SetSingleMethod(this->TrainTreesCallback,data.get()); threader->SingleMethodExecute(); // set result trees m_RandomForest.set_options().tree_count(m_Parameter->TreeCount); m_RandomForest.ext_param_.class_count_ = data->m_ClassCount; m_RandomForest.trees_ = data->trees_; // Set Tree Weights to default m_TreeWeights = Eigen::MatrixXd(m_Parameter->TreeCount,1); m_TreeWeights.fill(1.0); } Eigen::MatrixXi mitk::VigraRandomForestClassifier::Predict(const Eigen::MatrixXd &X_in) { // Initialize output Eigen matrices m_OutProbability = Eigen::MatrixXd(X_in.rows(),m_RandomForest.class_count()); m_OutProbability.fill(0); m_OutLabel = Eigen::MatrixXi(X_in.rows(),1); m_OutLabel.fill(0); // If no weights provided if(m_TreeWeights.rows() != m_RandomForest.tree_count()) { m_TreeWeights = Eigen::MatrixXd(m_RandomForest.tree_count(),1); m_TreeWeights.fill(1); } vigra::MultiArrayView<2, double> P(vigra::Shape2(m_OutProbability.rows(),m_OutProbability.cols()),m_OutProbability.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(m_OutLabel.rows(),m_OutLabel.cols()),m_OutLabel.data()); vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, double> TW(vigra::Shape2(m_RandomForest.tree_count(),1),m_TreeWeights.data()); std::unique_ptr data; data.reset(new PredictionData(m_RandomForest, X, Y, P, TW)); - itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); + auto threader = itk::MultiThreaderBase::New(); threader->SetSingleMethod(this->PredictCallback, data.get()); threader->SingleMethodExecute(); m_Probabilities = data->m_Probabilities; return m_OutLabel; } Eigen::MatrixXi mitk::VigraRandomForestClassifier::PredictWeighted(const Eigen::MatrixXd &X_in) { // Initialize output Eigen matrices m_OutProbability = Eigen::MatrixXd(X_in.rows(),m_RandomForest.class_count()); m_OutProbability.fill(0); m_OutLabel = Eigen::MatrixXi(X_in.rows(),1); m_OutLabel.fill(0); // If no weights provided if(m_TreeWeights.rows() != m_RandomForest.tree_count()) { m_TreeWeights = Eigen::MatrixXd(m_RandomForest.tree_count(),1); m_TreeWeights.fill(1); } vigra::MultiArrayView<2, double> P(vigra::Shape2(m_OutProbability.rows(),m_OutProbability.cols()),m_OutProbability.data()); vigra::MultiArrayView<2, int> Y(vigra::Shape2(m_OutLabel.rows(),m_OutLabel.cols()),m_OutLabel.data()); vigra::MultiArrayView<2, double> X(vigra::Shape2(X_in.rows(),X_in.cols()),X_in.data()); vigra::MultiArrayView<2, double> TW(vigra::Shape2(m_RandomForest.tree_count(),1),m_TreeWeights.data()); std::unique_ptr data; data.reset( new PredictionData(m_RandomForest,X,Y,P,TW)); - itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); + auto threader = itk::MultiThreaderBase::New(); threader->SetSingleMethod(this->PredictWeightedCallback,data.get()); threader->SingleMethodExecute(); return m_OutLabel; } void mitk::VigraRandomForestClassifier::SetTreeWeights(Eigen::MatrixXd weights) { m_TreeWeights = weights; } Eigen::MatrixXd mitk::VigraRandomForestClassifier::GetTreeWeights() const { return m_TreeWeights; } -ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::TrainTreesCallback(void * arg) +itk::ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::TrainTreesCallback(void * arg) { // Get the ThreadInfoStruct - typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType; + typedef itk::MultiThreaderBase::WorkUnitInfo ThreadInfoType; ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg ); TrainingData * data = (TrainingData *)(infoStruct->UserData); unsigned int numberOfTreesToCalculate = 0; // define the number of tress the forest have to calculate - numberOfTreesToCalculate = data->m_NumberOfTrees / infoStruct->NumberOfThreads; + numberOfTreesToCalculate = data->m_NumberOfTrees / infoStruct->NumberOfWorkUnits; // the 0th thread takes the residuals - if(infoStruct->ThreadID == 0) numberOfTreesToCalculate += data->m_NumberOfTrees % infoStruct->NumberOfThreads; + if(infoStruct->WorkUnitID == 0) numberOfTreesToCalculate += data->m_NumberOfTrees % infoStruct->NumberOfWorkUnits; if(numberOfTreesToCalculate != 0){ // Copy the Treestructure defined in userData vigra::RandomForest rf = data->m_RandomForest; // Initialize a splitter for the leraning process DefaultSplitType splitter; splitter.UsePointBasedWeights(data->m_Splitter.IsUsingPointBasedWeights()); splitter.UseRandomSplit(data->m_Splitter.IsUsingRandomSplit()); splitter.SetPrecision(data->m_Splitter.GetPrecision()); splitter.SetMaximumTreeDepth(data->m_Splitter.GetMaximumTreeDepth()); splitter.SetWeights(data->m_Splitter.GetWeights()); rf.trees_.clear(); rf.set_options().tree_count(numberOfTreesToCalculate); rf.set_options().use_stratification(data->m_Parameter.Stratification); rf.set_options().sample_with_replacement(data->m_Parameter.SampleWithReplacement); rf.set_options().samples_per_tree(data->m_Parameter.SamplesPerTree); rf.set_options().min_split_node_size(data->m_Parameter.MinimumSplitNodeSize); rf.learn(data->m_Feature, data->m_Label,vigra::rf::visitors::VisitorBase(),splitter); - data->m_mutex->Lock(); + data->m_mutex.lock(); for(const auto & tree : rf.trees_) data->trees_.push_back(tree); data->m_ClassCount = rf.class_count(); - data->m_mutex->Unlock(); + data->m_mutex.unlock(); } - return ITK_THREAD_RETURN_VALUE; + return ITK_THREAD_RETURN_DEFAULT_VALUE; } -ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictCallback(void * arg) +itk::ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictCallback(void * arg) { // Get the ThreadInfoStruct - typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType; + typedef itk::MultiThreaderBase::WorkUnitInfo ThreadInfoType; ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg ); // assigne the thread id - const unsigned int threadId = infoStruct->ThreadID; + const unsigned int threadId = infoStruct->WorkUnitID; // Get the user defined parameters containing all // neccesary informations PredictionData * data = (PredictionData *)(infoStruct->UserData); unsigned int numberOfRowsToCalculate = 0; // Get number of rows to calculate - numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfThreads; + numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfWorkUnits; unsigned int start_index = numberOfRowsToCalculate * threadId; unsigned int end_index = numberOfRowsToCalculate * (threadId+1); // the last thread takes the residuals - if(threadId == infoStruct->NumberOfThreads-1) { - end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfThreads; + if(threadId == infoStruct->NumberOfWorkUnits-1) { + end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfWorkUnits; } vigra::MultiArrayView<2, double> split_features; vigra::MultiArrayView<2, int> split_labels; vigra::MultiArrayView<2, double> split_probability; { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Feature.shape(1)); split_features = data->m_Feature.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index, data->m_Label.shape(1)); split_labels = data->m_Label.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Probabilities.shape(1)); split_probability = data->m_Probabilities.subarray(lowerBound,upperBound); } data->m_RandomForest.predictLabels(split_features,split_labels); data->m_RandomForest.predictProbabilities(split_features, split_probability); - return ITK_THREAD_RETURN_VALUE; + return ITK_THREAD_RETURN_DEFAULT_VALUE; } -ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictWeightedCallback(void * arg) +itk::ITK_THREAD_RETURN_TYPE mitk::VigraRandomForestClassifier::PredictWeightedCallback(void * arg) { // Get the ThreadInfoStruct - typedef itk::MultiThreader::ThreadInfoStruct ThreadInfoType; + typedef itk::MultiThreaderBase::WorkUnitInfo ThreadInfoType; ThreadInfoType * infoStruct = static_cast< ThreadInfoType * >( arg ); // assigne the thread id - const unsigned int threadId = infoStruct->ThreadID; + const unsigned int threadId = infoStruct->WorkUnitID; // Get the user defined parameters containing all // neccesary informations PredictionData * data = (PredictionData *)(infoStruct->UserData); unsigned int numberOfRowsToCalculate = 0; // Get number of rows to calculate - numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfThreads; + numberOfRowsToCalculate = data->m_Feature.shape()[0] / infoStruct->NumberOfWorkUnits; unsigned int start_index = numberOfRowsToCalculate * threadId; unsigned int end_index = numberOfRowsToCalculate * (threadId+1); // the last thread takes the residuals - if(threadId == infoStruct->NumberOfThreads-1) { - end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfThreads; + if(threadId == infoStruct->NumberOfWorkUnits-1) { + end_index += data->m_Feature.shape()[0] % infoStruct->NumberOfWorkUnits; } vigra::MultiArrayView<2, double> split_features; vigra::MultiArrayView<2, int> split_labels; vigra::MultiArrayView<2, double> split_probability; { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Feature.shape(1)); split_features = data->m_Feature.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index, data->m_Label.shape(1)); split_labels = data->m_Label.subarray(lowerBound,upperBound); } { vigra::TinyVector lowerBound(start_index,0); vigra::TinyVector upperBound(end_index,data->m_Probabilities.shape(1)); split_probability = data->m_Probabilities.subarray(lowerBound,upperBound); } VigraPredictWeighted(data, split_features,split_labels,split_probability); - return ITK_THREAD_RETURN_VALUE; + return ITK_THREAD_RETURN_DEFAULT_VALUE; } void mitk::VigraRandomForestClassifier::VigraPredictWeighted(PredictionData * data, vigra::MultiArrayView<2, double> & X, vigra::MultiArrayView<2, int> & Y, vigra::MultiArrayView<2, double> & P) { int isSampleWeighted = data->m_RandomForest.options_.predict_weighted_; //#pragma omp parallel for for(int row=0; row < vigra::rowCount(X); ++row) { vigra::MultiArrayView<2, double, vigra::StridedArrayTag> currentRow(rowVector(X, row)); vigra::ArrayVector::const_iterator weights; //totalWeight == totalVoteCount! double totalWeight = 0.0; //Let each tree classify... for(int k=0; km_RandomForest.options_.tree_count_; ++k) { //get weights predicted by single tree weights = data->m_RandomForest.trees_[k /*tree_indices_[k]*/].predict(currentRow); double numberOfLeafObservations = (*(weights-1)); //update votecount. for(int l=0; lm_RandomForest.ext_param_.class_count_; ++l) { // Either the original weights are taken or the tree is additional weighted by the number of Observations in the leaf node. double cur_w = weights[l] * (isSampleWeighted * numberOfLeafObservations + (1-isSampleWeighted)); cur_w = cur_w * data->m_TreeWeights(k,0); P(row, l) += (int)cur_w; //every weight in totalWeight. totalWeight += cur_w; } } //Normalise votes in each row by total VoteCount (totalWeight for(int l=0; l< data->m_RandomForest.ext_param_.class_count_; ++l) { P(row, l) /= vigra::detail::RequiresExplicitCast::cast(totalWeight); } int erg; int maxCol = 0; for (int col=0;colm_RandomForest.class_count();++col) { if (data->m_Probabilities(row,col) > data->m_Probabilities(row, maxCol)) maxCol = col; } data->m_RandomForest.ext_param_.to_classlabel(maxCol, erg); Y(row,0) = erg; } } void mitk::VigraRandomForestClassifier::ConvertParameter() { if(this->m_Parameter == nullptr) this->m_Parameter = new Parameter(); // Get the proerty // Some defaults MITK_INFO("VigraRandomForestClassifier") << "Convert Parameter"; if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights)) this->m_Parameter->UsePointBasedWeights = false; if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit)) this->m_Parameter->UseRandomSplit = false; if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth)) this->m_Parameter->TreeDepth = 20; if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount)) this->m_Parameter->TreeCount = 100; if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize)) this->m_Parameter->MinimumSplitNodeSize = 5; if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision)) this->m_Parameter->Precision = mitk::eps; if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree)) this->m_Parameter->SamplesPerTree = 0.6; if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement)) this->m_Parameter->SampleWithReplacement = true; if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda)) this->m_Parameter->WeightLambda = 1.0; // Not used yet // if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification)) this->m_Parameter->Stratification = vigra::RF_NONE; // no Property given } void mitk::VigraRandomForestClassifier::PrintParameter(std::ostream & str) { if(this->m_Parameter == nullptr) { MITK_WARN("VigraRandomForestClassifier") << "Parameters are not initialized. Please call ConvertParameter() first!"; return; } this->ConvertParameter(); // Get the proerty // Some defaults if(!this->GetPropertyList()->Get("usepointbasedweight",this->m_Parameter->UsePointBasedWeights)) str << "usepointbasedweight\tNOT SET (default " << this->m_Parameter->UsePointBasedWeights << ")" << "\n"; else str << "usepointbasedweight\t" << this->m_Parameter->UsePointBasedWeights << "\n"; if(!this->GetPropertyList()->Get("userandomsplit",this->m_Parameter->UseRandomSplit)) str << "userandomsplit\tNOT SET (default " << this->m_Parameter->UseRandomSplit << ")" << "\n"; else str << "userandomsplit\t" << this->m_Parameter->UseRandomSplit << "\n"; if(!this->GetPropertyList()->Get("treedepth",this->m_Parameter->TreeDepth)) str << "treedepth\t\tNOT SET (default " << this->m_Parameter->TreeDepth << ")" << "\n"; else str << "treedepth\t\t" << this->m_Parameter->TreeDepth << "\n"; if(!this->GetPropertyList()->Get("minimalsplitnodesize",this->m_Parameter->MinimumSplitNodeSize)) str << "minimalsplitnodesize\tNOT SET (default " << this->m_Parameter->MinimumSplitNodeSize << ")" << "\n"; else str << "minimalsplitnodesize\t" << this->m_Parameter->MinimumSplitNodeSize << "\n"; if(!this->GetPropertyList()->Get("precision",this->m_Parameter->Precision)) str << "precision\t\tNOT SET (default " << this->m_Parameter->Precision << ")" << "\n"; else str << "precision\t\t" << this->m_Parameter->Precision << "\n"; if(!this->GetPropertyList()->Get("samplespertree",this->m_Parameter->SamplesPerTree)) str << "samplespertree\tNOT SET (default " << this->m_Parameter->SamplesPerTree << ")" << "\n"; else str << "samplespertree\t" << this->m_Parameter->SamplesPerTree << "\n"; if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->SampleWithReplacement)) str << "samplewithreplacement\tNOT SET (default " << this->m_Parameter->SampleWithReplacement << ")" << "\n"; else str << "samplewithreplacement\t" << this->m_Parameter->SampleWithReplacement << "\n"; if(!this->GetPropertyList()->Get("treecount",this->m_Parameter->TreeCount)) str << "treecount\t\tNOT SET (default " << this->m_Parameter->TreeCount << ")" << "\n"; else str << "treecount\t\t" << this->m_Parameter->TreeCount << "\n"; if(!this->GetPropertyList()->Get("lambda",this->m_Parameter->WeightLambda)) str << "lambda\t\tNOT SET (default " << this->m_Parameter->WeightLambda << ")" << "\n"; else str << "lambda\t\t" << this->m_Parameter->WeightLambda << "\n"; // if(!this->GetPropertyList()->Get("samplewithreplacement",this->m_Parameter->Stratification)) // this->m_Parameter->Stratification = vigra:RF_NONE; // no Property given } void mitk::VigraRandomForestClassifier::UsePointWiseWeight(bool val) { mitk::AbstractClassifier::UsePointWiseWeight(val); this->GetPropertyList()->SetBoolProperty("usepointbasedweight",val); } void mitk::VigraRandomForestClassifier::SetMaximumTreeDepth(int val) { this->GetPropertyList()->SetIntProperty("treedepth",val); } void mitk::VigraRandomForestClassifier::SetMinimumSplitNodeSize(int val) { this->GetPropertyList()->SetIntProperty("minimalsplitnodesize",val); } void mitk::VigraRandomForestClassifier::SetPrecision(double val) { this->GetPropertyList()->SetDoubleProperty("precision",val); } void mitk::VigraRandomForestClassifier::SetSamplesPerTree(double val) { this->GetPropertyList()->SetDoubleProperty("samplespertree",val); } void mitk::VigraRandomForestClassifier::UseSampleWithReplacement(bool val) { this->GetPropertyList()->SetBoolProperty("samplewithreplacement",val); } void mitk::VigraRandomForestClassifier::SetTreeCount(int val) { this->GetPropertyList()->SetIntProperty("treecount",val); } void mitk::VigraRandomForestClassifier::SetWeightLambda(double val) { this->GetPropertyList()->SetDoubleProperty("lambda",val); } void mitk::VigraRandomForestClassifier::SetTreeWeight(int treeId, double weight) { m_TreeWeights(treeId,0) = weight; } void mitk::VigraRandomForestClassifier::SetRandomForest(const vigra::RandomForest & rf) { this->SetMaximumTreeDepth(rf.ext_param().max_tree_depth); this->SetMinimumSplitNodeSize(rf.options().min_split_node_size_); this->SetTreeCount(rf.options().tree_count_); this->SetSamplesPerTree(rf.options().training_set_proportion_); this->UseSampleWithReplacement(rf.options().sample_with_replacement_); this->m_RandomForest = rf; } const vigra::RandomForest & mitk::VigraRandomForestClassifier::GetRandomForest() const { return this->m_RandomForest; } diff --git a/Modules/ContourModel/DataManagement/mitkContourElement.cpp b/Modules/ContourModel/DataManagement/mitkContourElement.cpp index be73ced97b..02cfc0c4a3 100644 --- a/Modules/ContourModel/DataManagement/mitkContourElement.cpp +++ b/Modules/ContourModel/DataManagement/mitkContourElement.cpp @@ -1,489 +1,489 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 bool mitk::ContourElement::ContourModelVertex::operator==(const ContourModelVertex &other) const { return this->Coordinates == other.Coordinates && this->IsControlPoint == other.IsControlPoint; } mitk::ContourElement::ConstVertexIterator mitk::ContourElement::ConstIteratorBegin() const { return this->begin(); } mitk::ContourElement::ConstVertexIterator mitk::ContourElement::ConstIteratorEnd() const { return this->end(); } mitk::ContourElement::VertexIterator mitk::ContourElement::IteratorBegin() { return this->begin(); } mitk::ContourElement::VertexIterator mitk::ContourElement::IteratorEnd() { return this->end(); } mitk::ContourElement::ConstVertexIterator mitk::ContourElement::begin() const { return this->m_Vertices.begin(); } mitk::ContourElement::ConstVertexIterator mitk::ContourElement::end() const { return this->m_Vertices.end(); } mitk::ContourElement::VertexIterator mitk::ContourElement::begin() { return this->m_Vertices.begin(); } mitk::ContourElement::VertexIterator mitk::ContourElement::end() { return this->m_Vertices.end(); } mitk::ContourElement::ContourElement(const mitk::ContourElement &other) : itk::LightObject(), m_IsClosed(other.m_IsClosed) { for (const auto &v : other.m_Vertices) { m_Vertices.push_back(new ContourModelVertex(*v)); } } mitk::ContourElement &mitk::ContourElement::operator=(const ContourElement &other) { if (this != &other) { this->Clear(); for (const auto &v : other.m_Vertices) { m_Vertices.push_back(new ContourModelVertex(*v)); } } this->m_IsClosed = other.m_IsClosed; return *this; } mitk::ContourElement::~ContourElement() { this->Clear(); } mitk::ContourElement::VertexSizeType mitk::ContourElement::GetSize() const { return this->m_Vertices.size(); } void mitk::ContourElement::AddVertex(const mitk::Point3D &vertex, bool isControlPoint) { this->m_Vertices.push_back(new VertexType(vertex, isControlPoint)); } void mitk::ContourElement::AddVertexAtFront(const mitk::Point3D &vertex, bool isControlPoint) { this->m_Vertices.push_front(new VertexType(vertex, isControlPoint)); } void mitk::ContourElement::InsertVertexAtIndex(const mitk::Point3D &vertex, bool isControlPoint, VertexSizeType index) { - if (index >= 0 && this->GetSize() > index) + if (this->GetSize() > index) { auto _where = this->m_Vertices.begin(); _where += index; this->m_Vertices.insert(_where, new VertexType(vertex, isControlPoint)); } } void mitk::ContourElement::SetVertexAt(VertexSizeType pointId, const Point3D &point) { - if (pointId >= 0 && this->GetSize() > pointId) + if (this->GetSize() > pointId) { this->m_Vertices[pointId]->Coordinates = point; } } void mitk::ContourElement::SetVertexAt(VertexSizeType pointId, const VertexType *vertex) { if (nullptr == vertex) { mitkThrow() << "Cannot set vertex. Passed vertex instance is invalid. Index to set: " << pointId; } - if (pointId >= 0 && this->GetSize() > pointId) + if (this->GetSize() > pointId) { this->m_Vertices[pointId]->Coordinates = vertex->Coordinates; this->m_Vertices[pointId]->IsControlPoint = vertex->IsControlPoint; } } mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(VertexSizeType index) { return this->m_Vertices.at(index); } const mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(VertexSizeType index) const { return this->m_Vertices.at(index); } bool mitk::ContourElement::IsEmpty() const { return this->m_Vertices.empty(); } mitk::ContourElement::VertexType *mitk::ContourElement::GetControlVertexAt(const mitk::Point3D &point, float eps) { /* current version iterates over the whole deque - should some kind of an octree with spatial query*/ if (eps > 0) { // currently no method with better performance is available return BruteForceGetVertexAt(point, eps, true); } // if eps < 0 return nullptr; } mitk::ContourElement::VertexType *mitk::ContourElement::GetVertexAt(const mitk::Point3D &point, float eps) { /* current version iterates over the whole deque - should some kind of an octree with spatial query*/ if (eps > 0) { // currently no method with better performance is available return BruteForceGetVertexAt(point, eps); } // if eps < 0 return nullptr; } mitk::ContourElement::VertexType *mitk::ContourElement::GetNextControlVertexAt(const mitk::Point3D &point, float eps) { /* current version iterates over the whole deque - should some kind of an octree with spatial query*/ if (eps > 0) { // currently no method with better performance is available return BruteForceGetVertexAt(point, eps, true, 1); } // if eps < 0 return nullptr; } mitk::ContourElement::VertexType *mitk::ContourElement::GetPreviousControlVertexAt(const mitk::Point3D &point, float eps) { /* current version iterates over the whole deque - should some kind of an octree with spatial query*/ if (eps > 0) { // currently no method with better performance is available return BruteForceGetVertexAt(point, eps, true, -1); } // if eps < 0 return nullptr; } mitk::ContourElement::VertexType *mitk::ContourElement::BruteForceGetVertexAt(const mitk::Point3D &point, double eps, bool isControlPoint, int offset) { VertexListType verticesList; if (isControlPoint) { verticesList = this->GetControlVertices(); } else { verticesList = *this->GetVertexList(); } int vertexIndex = BruteForceGetVertexIndexAt(point, eps, verticesList); if (vertexIndex!=-1) { vertexIndex += offset; if (vertexIndex < 0) { // for negative offset // if the offset exceeds the first vertex, we start from the end of the vertex list backwards vertexIndex = verticesList.size() + offset; } else if (vertexIndex >= (int) verticesList.size()) { // if the offset exceeds the last vertex, we start from the beginning of the vertex list vertexIndex = vertexIndex - verticesList.size(); } return verticesList[vertexIndex]; } return nullptr; } int mitk::ContourElement::BruteForceGetVertexIndexAt(const mitk::Point3D &point, double eps, VertexListType verticesList) { if (eps < 0) { mitkThrow() << "Distance cannot be negative"; } ConstVertexIterator nearestPointIterator; bool nearestPointIsInitialized = false; double nearestPointDistance = std::numeric_limits::max(); ConstVertexIterator it = verticesList.begin(); ConstVertexIterator end = verticesList.end(); while (it != end) { mitk::Point3D currentPoint = (*it)->Coordinates; double distance = currentPoint.EuclideanDistanceTo(point); if (distance < eps) { if (distance < nearestPointDistance) { nearestPointIterator = it; nearestPointIsInitialized = true; nearestPointDistance = distance; } } // if distance > eps it++; } // while if (nearestPointIsInitialized) { return nearestPointIterator - verticesList.begin(); } return -1; } const mitk::ContourElement::VertexListType *mitk::ContourElement::GetVertexList() const { return &(this->m_Vertices); } bool mitk::ContourElement::IsClosed() const { return this->m_IsClosed; } bool mitk::ContourElement::IsNearContour(const mitk::Point3D &point, float eps) const { ConstVertexIterator it1 = this->m_Vertices.begin(); ConstVertexIterator it2 = this->m_Vertices.begin(); it2++; // it2 runs one position ahead ConstVertexIterator end = this->m_Vertices.end(); int counter = 0; for (; it1 != end; it1++, it2++, counter++) { if (it2 == end) it2 = this->m_Vertices.begin(); mitk::Point3D v1 = (*it1)->Coordinates; mitk::Point3D v2 = (*it2)->Coordinates; const float l2 = v1.SquaredEuclideanDistanceTo(v2); mitk::Vector3D p_v1 = point - v1; mitk::Vector3D v2_v1 = v2 - v1; double tc = (p_v1 * v2_v1) / l2; // take into account we have line segments and not (infinite) lines if (tc < 0.0) tc = 0.0; if (tc > 1.0) tc = 1.0; mitk::Point3D crossPoint = v1 + v2_v1 * tc; double distance = point.SquaredEuclideanDistanceTo(crossPoint); if (distance < eps) { return true; } } return false; } void mitk::ContourElement::Close() { this->m_IsClosed = true; } void mitk::ContourElement::Open() { this->m_IsClosed = false; } void mitk::ContourElement::SetClosed(bool isClosed) { isClosed ? this->Close() : this->Open(); } mitk::ContourElement::VertexListType mitk::ContourElement::GetControlVertices() const { VertexListType controlVertices; std::copy_if( this->m_Vertices.begin(), this->m_Vertices.end(), std::back_inserter(controlVertices), [](const VertexType *v) { return v->IsControlPoint; }); return controlVertices; } void mitk::ContourElement::Concatenate(const mitk::ContourElement *other, bool check) { if (other->GetSize() > 0) { for (const auto &sourceVertex : other->m_Vertices) { if (check) { auto finding = std::find_if(this->m_Vertices.begin(), this->m_Vertices.end(), [sourceVertex](const VertexType *v) { return sourceVertex->Coordinates == v->Coordinates; }); if (finding == this->m_Vertices.end()) { this->m_Vertices.push_back(new ContourModelVertex(*sourceVertex)); } } else { this->m_Vertices.push_back(new ContourModelVertex(*sourceVertex)); } } } } mitk::ContourElement::VertexSizeType mitk::ContourElement::GetIndex(const VertexType *vertex) const { VertexSizeType result = NPOS; auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), vertex); if (finding != this->m_Vertices.end()) { result = finding - this->m_Vertices.begin(); } return result; } bool mitk::ContourElement::RemoveVertex(const VertexType *vertex) { auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), vertex); return RemoveVertexByIterator(finding); } bool mitk::ContourElement::RemoveVertexAt(VertexSizeType index) { - if (index >= 0 && index < this->m_Vertices.size()) + if (index < this->m_Vertices.size()) { auto delIter = this->m_Vertices.begin() + index; return RemoveVertexByIterator(delIter); } return false; } bool mitk::ContourElement::RemoveVertexAt(const mitk::Point3D &point, double eps) { if (eps > 0) { auto finding = std::find_if(this->m_Vertices.begin(), this->m_Vertices.end(), [point, eps](const VertexType *v) { return v->Coordinates.EuclideanDistanceTo(point) < eps; }); return RemoveVertexByIterator(finding); } return false; } bool mitk::ContourElement::RemoveVertexByIterator(VertexListType::iterator &iter) { if (iter != this->m_Vertices.end()) { delete *iter; this->m_Vertices.erase(iter); return true; } return false; } void mitk::ContourElement::Clear() { for (auto vertex : m_Vertices) { delete vertex; } this->m_Vertices.clear(); } //---------------------------------------------------------------------- void mitk::ContourElement::RedistributeControlVertices(const VertexType *selected, int period) { int counter = 0; auto _where = this->m_Vertices.begin(); if (selected != nullptr) { auto finding = std::find(this->m_Vertices.begin(), this->m_Vertices.end(), selected); if (finding != this->m_Vertices.end()) { _where = finding; } } auto _iter = _where; while (_iter != this->m_Vertices.end()) { div_t divresult; divresult = div(counter, period); (*_iter)->IsControlPoint = (divresult.rem == 0); counter++; _iter++; } _iter = _where; counter = 0; while (_iter != this->m_Vertices.begin()) { div_t divresult; divresult = div(counter, period); (*_iter)->IsControlPoint = (divresult.rem == 0); counter++; _iter--; } } diff --git a/Modules/ContourModel/DataManagement/mitkContourElement.h b/Modules/ContourModel/DataManagement/mitkContourElement.h index 9a9f17186e..4a5e52864e 100644 --- a/Modules/ContourModel/DataManagement/mitkContourElement.h +++ b/Modules/ContourModel/DataManagement/mitkContourElement.h @@ -1,289 +1,290 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 _mitkContourElement_H_ #define _mitkContourElement_H_ #include "mitkCommon.h" #include #include #include namespace mitk { /** \brief Represents a contour in 3D space. A ContourElement is consisting of linked vertices implicitely defining the contour. They are stored in a double ended queue making it possible to add vertices at front and end of the contour and to iterate in both directions. To mark a vertex as a special one it can be set as a control point. \note This class assumes that it manages its vertices. So if a vertex instance is added to this class the ownership of the vertex is transfered to the ContourElement instance. The ContourElement instance takes care of deleting vertex instances if needed. It is highly not recommend to use this class directly as it is designed as a internal class of ContourModel. Therefore it is adviced to use ContourModel if contour representations are needed in MITK. */ class MITKCONTOURMODEL_EXPORT ContourElement : public itk::LightObject { public: mitkClassMacroItkParent(ContourElement, itk::LightObject); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** \brief Represents a single vertex of a contour. */ struct MITKCONTOURMODEL_EXPORT ContourModelVertex { ContourModelVertex(const mitk::Point3D& point, bool active = false) : IsControlPoint(active), Coordinates(point) {}; ContourModelVertex(const ContourModelVertex& other) : IsControlPoint(other.IsControlPoint), Coordinates(other.Coordinates) { }; /** \brief Treat point special. */ bool IsControlPoint; /** \brief Coordinates in 3D space. */ mitk::Point3D Coordinates; bool operator ==(const ContourModelVertex& other) const; }; using VertexType = ContourModelVertex; using VertexListType = std::deque; using VertexIterator = VertexListType::iterator; using ConstVertexIterator = VertexListType::const_iterator; using VertexSizeType = VertexListType::size_type; /**Indicates an invalid index. * It is always the maximum of the unsigned int type.*/ static const VertexSizeType NPOS = -1; /** \brief Return a const iterator a the front. */ ConstVertexIterator ConstIteratorBegin() const; /** \brief Return a const iterator a the end. */ ConstVertexIterator ConstIteratorEnd() const; /** \brief Return an iterator a the front. */ VertexIterator IteratorBegin(); /** \brief Return an iterator a the end. */ VertexIterator IteratorEnd(); /** \brief Return a const iterator a the front. * For easier support of stl functionality. */ ConstVertexIterator begin() const; /** \brief Return a const iterator a the end. * For easier support of stl functionality. */ ConstVertexIterator end() const; /** \brief Return an iterator a the front. * For easier support of stl functionality. */ VertexIterator begin(); /** \brief Return an iterator a the end. * For easier support of stl functionality. */ VertexIterator end(); /** \brief Returns the number of contained vertices. */ VertexSizeType GetSize() const; /** \brief Add a vertex at the end of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a special control point. */ void AddVertex(const mitk::Point3D &point, bool isControlPoint); /** \brief Add a vertex at the front of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a control point. */ void AddVertexAtFront(const mitk::Point3D &point, bool isControlPoint); /** \brief Add a vertex at a given index of the contour \param point - coordinates in 3D space. \param isControlPoint - is the vertex a special control point. \param index - the index to be inserted at. */ void InsertVertexAtIndex(const mitk::Point3D &point, bool isControlPoint, VertexSizeType index); /** \brief Set coordinates a given index. \param pointId Index of vertex. \param point Coordinates. */ void SetVertexAt(VertexSizeType pointId, const mitk::Point3D &point); /** \brief Set vertex a given index (by copying the values). \param pointId Index of vertex. \param vertex Vertex. \pre Passed vertex is a valid instance */ void SetVertexAt(VertexSizeType pointId, const VertexType* vertex); /** \brief Returns the vertex a given index \param index \pre index must be valid. */ VertexType* GetVertexAt(VertexSizeType index); const VertexType* GetVertexAt(VertexSizeType index) const; /** \brief Returns the approximate nearest vertex a given position in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ VertexType *GetVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the next vertex to the approximate nearest vertex of a given position in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ VertexType *GetNextControlVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the previous vertex to the approximate nearest vertex of a given position in 3D space \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ VertexType *GetPreviousControlVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the approximate nearest control vertex a given posoition in 3D space, if the clicked position is within a specific range. \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ VertexType *GetControlVertexAt(const mitk::Point3D &point, float eps); /** \brief Returns the index of the given vertex within the contour. \param vertex - the vertex to be searched. \return index of vertex. Returns ContourElement::NPOS if not found. */ VertexSizeType GetIndex(const VertexType *vertex) const; /** \brief Returns the container of the vertices. */ const VertexListType *GetVertexList() const; /** \brief Returns whether the contour element is empty. */ bool IsEmpty() const; /** \brief Returns if the conour is closed or not. */ bool IsClosed() const; /** \brief Returns whether a given point is near a contour, according to eps. \param point - query position in 3D space. \param eps - the error bound for search algorithm. */ bool IsNearContour(const mitk::Point3D &point, float eps) const; /** \brief Close the contour. Connect first with last element. */ void Close(); /** \brief Open the contour. Disconnect first and last element. */ void Open(); /** \brief Set the contours IsClosed property. \param isClosed - true = closed; false = open; */ void SetClosed(bool isClosed); /** \brief Concatenate the contuor with a another contour. All vertices of the other contour will be cloned and added after last vertex. \param other - the other contour \param check - set it true to avoid adding of vertices that are already in the source contour */ void Concatenate(const mitk::ContourElement *other, bool check); /** \brief Remove the given vertex from the container if exists. \param vertex - the vertex to be removed. */ bool RemoveVertex(const VertexType *vertex); /** \brief Remove a vertex at given index within the container if exists. \param index - the index where the vertex should be removed. */ bool RemoveVertexAt(VertexSizeType index); /** \brief Remove the approximate nearest vertex at given position in 3D space if one exists. \param point - query point in 3D space. \param eps - error bound for search algorithm. */ bool RemoveVertexAt(const mitk::Point3D &point, double eps); /** \brief Clear the storage container. */ void Clear(); /** \brief Returns the approximate nearest vertex a given position in 3D space. With the parameter 'isControlPoint', one can decide if any vertex should be returned, or just control vertices. \param point - query position in 3D space. \param eps - the error bound for search algorithm. It is an open boundary. + \param isControlPoint \param offset - a offset to the vertex, e.g. 1 if the next vertex should be returned or -1 for the previous vertex */ VertexType *BruteForceGetVertexAt(const mitk::Point3D &point, double eps, bool isControlPoint = false, int offset = 0); /** \brief Returns the index of the approximate nearest vertex of a given position in 3D space. \param point - query position in 3D space. \param eps - the error bound for search algorithm. It is an open boundary. \param verticesList - the vertex list to search the index in, either only control vertices or all vertices */ int BruteForceGetVertexIndexAt(const mitk::Point3D &point, double eps, VertexListType verticesList); /** Returns a list pointing to all vertices that are indicated to be control points. \remark It is important to note, that the vertex pointers in the returned list directly point to the vertices stored interanlly. So they are still owned by the ContourElement instance that returns the list. If one wants to take over ownership, one has to clone the vertex instances. */ VertexListType GetControlVertices() const; /** \brief Uniformly redistribute control points with a given period (in number of vertices) \param vertex - the vertex around which the redistribution is done. \param period - number of vertices between control points. */ void RedistributeControlVertices(const VertexType *vertex, int period); protected: mitkCloneMacro(Self); ContourElement() = default; ContourElement(const mitk::ContourElement &other); ~ContourElement(); ContourElement& operator = (const ContourElement & other); /** Internal helper function to correctly remove the element indicated by the iterator from the list. After the call the iterator is invalid. Caller of the function must ensure that the iterator is valid!. \result Indicates if the element indicated by the iterator was removed. If iterator points to end it returns false.*/ bool RemoveVertexByIterator(VertexListType::iterator& iter); VertexListType m_Vertices; // double ended queue with vertices bool m_IsClosed = false; }; } // namespace mitk #endif // _mitkContourElement_H_ diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.cpp b/Modules/ContourModel/DataManagement/mitkContourModel.cpp index 02350613d6..518abf4352 100644 --- a/Modules/ContourModel/DataManagement/mitkContourModel.cpp +++ b/Modules/ContourModel/DataManagement/mitkContourModel.cpp @@ -1,679 +1,690 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 +namespace mitk +{ + itkEventMacroDefinition(ContourModelEvent, itk::AnyEvent); + itkEventMacroDefinition(ContourModelShiftEvent, ContourModelEvent); + itkEventMacroDefinition(ContourModelSizeChangeEvent, ContourModelEvent); + itkEventMacroDefinition(ContourModelAddEvent, ContourModelSizeChangeEvent); + itkEventMacroDefinition(ContourModelRemoveEvent, ContourModelSizeChangeEvent); + itkEventMacroDefinition(ContourModelExpandTimeBoundsEvent, ContourModelEvent); + itkEventMacroDefinition(ContourModelClosedEvent, ContourModelEvent); +} + mitk::ContourModel::ContourModel() : m_UpdateBoundingBox(true) { // set to initial state this->InitializeEmpty(); } mitk::ContourModel::ContourModel(const ContourModel &other) : BaseData(other), m_ContourSeries(other.m_ContourSeries), m_lineInterpolation(other.m_lineInterpolation) { m_SelectedVertex = nullptr; } mitk::ContourModel::~ContourModel() { m_SelectedVertex = nullptr; this->m_ContourSeries.clear(); // TODO check destruction } void mitk::ContourModel::AddVertex(const Point3D &vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->AddVertex(vertex, false, timestep); } } void mitk::ContourModel::AddVertex(const Point3D &vertex, bool isControlPoint, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertex(vertex, isControlPoint); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::AddVertex(const VertexType &vertex, TimeStepType timestep) { this->AddVertex(vertex.Coordinates, vertex.IsControlPoint, timestep); } void mitk::ContourModel::AddVertexAtFront(const Point3D &vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->AddVertexAtFront(vertex, false, timestep); } } void mitk::ContourModel::AddVertexAtFront(const Point3D &vertex, bool isControlPoint, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->AddVertexAtFront(vertex, isControlPoint); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::AddVertexAtFront(const VertexType &vertex, TimeStepType timestep) { this->AddVertexAtFront(vertex.Coordinates, vertex.IsControlPoint, timestep); } bool mitk::ContourModel::SetVertexAt(int pointId, const Point3D &point, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (pointId >= 0 && this->m_ContourSeries[timestep]->GetSize() > ContourElement::VertexSizeType(pointId)) { this->m_ContourSeries[timestep]->SetVertexAt(pointId, point); this->Modified(); this->m_UpdateBoundingBox = true; return true; } return false; } return false; } bool mitk::ContourModel::SetVertexAt(int pointId, const VertexType *vertex, TimeStepType timestep) { if (vertex == nullptr) return false; if (!this->IsEmptyTimeStep(timestep)) { if (pointId >= 0 && this->m_ContourSeries[timestep]->GetSize() > ContourElement::VertexSizeType(pointId)) { this->m_ContourSeries[timestep]->SetVertexAt(pointId, vertex); this->Modified(); this->m_UpdateBoundingBox = true; return true; } return false; } return false; } void mitk::ContourModel::InsertVertexAtIndex(const Point3D &vertex, int index, bool isControlPoint, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (index >= 0 && this->m_ContourSeries[timestep]->GetSize() > ContourElement::VertexSizeType(index)) { this->m_ContourSeries[timestep]->InsertVertexAtIndex(vertex, isControlPoint, index); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } } void mitk::ContourModel::UpdateContour(const ContourModel* sourceModel, TimeStepType destinationTimeStep, TimeStepType sourceTimeStep) { if (nullptr == sourceModel) { mitkThrow() << "Cannot update contour. Passed source model is invalid."; } if (!sourceModel->GetTimeGeometry()->IsValidTimeStep(sourceTimeStep)) { mitkThrow() << "Cannot update contour. Source contour time geometry does not support passed time step. Invalid time step: " << sourceTimeStep; } if (!this->GetTimeGeometry()->IsValidTimeStep(destinationTimeStep)) { MITK_WARN << "Cannot update contour. Contour time geometry does not support passed time step. Invalid time step: " << destinationTimeStep; return; } this->Clear(destinationTimeStep); std::for_each(sourceModel->Begin(sourceTimeStep), sourceModel->End(sourceTimeStep), [this, destinationTimeStep](ContourElement::VertexType* vertex) { this->m_ContourSeries[destinationTimeStep]->AddVertex(vertex->Coordinates, vertex->IsControlPoint); }); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } bool mitk::ContourModel::IsEmpty(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsEmpty(); } return true; } bool mitk::ContourModel::IsEmpty() const { return this->IsEmpty(0); } int mitk::ContourModel::GetNumberOfVertices(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetSize(); } return -1; } const mitk::ContourModel::VertexType *mitk::ContourModel::GetVertexAt(int index, TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep) && this->m_ContourSeries[timestep]->GetSize() > mitk::ContourElement::VertexSizeType(index)) { return this->m_ContourSeries[timestep]->GetVertexAt(index); } return nullptr; } const mitk::ContourModel::VertexType *mitk::ContourModel::GetNextControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetNextControlVertexAt(point, eps); } return nullptr; } const mitk::ContourModel::VertexType *mitk::ContourModel::GetPreviousControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetPreviousControlVertexAt(point, eps); } return nullptr; } int mitk::ContourModel::GetIndex(const VertexType *vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->GetIndex(vertex); } return -1; } void mitk::ContourModel::Close(TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->Close(); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::Open(TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->Open(); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::SetClosed(bool isClosed, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->SetClosed(isClosed); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } bool mitk::ContourModel::IsEmptyTimeStep(unsigned int t) const { return (this->m_ContourSeries.size() <= t); } bool mitk::ContourModel::IsNearContour(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsNearContour(point, eps); } return false; } void mitk::ContourModel::Concatenate(ContourModel *other, TimeStepType timestep, bool check) { if (!this->IsEmptyTimeStep(timestep)) { if (!this->m_ContourSeries[timestep]->IsClosed()) { this->m_ContourSeries[timestep]->Concatenate(other->m_ContourSeries[timestep], check); this->InvokeEvent(ContourModelSizeChangeEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } } mitk::ContourModel::VertexIterator mitk::ContourModel::Begin(TimeStepType timestep) const { return this->IteratorBegin(timestep); } mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorBegin(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IteratorBegin(); } else { mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps() << " timesteps available."; } } mitk::ContourModel::VertexIterator mitk::ContourModel::End(TimeStepType timestep) const { return this->IteratorEnd(timestep); } mitk::ContourModel::VertexIterator mitk::ContourModel::IteratorEnd(TimeStepType timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IteratorEnd(); } else { mitkThrow() << "No iterator at invalid timestep " << timestep << ". There are only " << this->GetTimeSteps() << " timesteps available."; } } bool mitk::ContourModel::IsClosed(int timestep) const { if (!this->IsEmptyTimeStep(timestep)) { return this->m_ContourSeries[timestep]->IsClosed(); } return false; } bool mitk::ContourModel::SelectControlVertexAt(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetControlVertexAt(point, eps); } return this->m_SelectedVertex != nullptr; } bool mitk::ContourModel::SelectVertexAt(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps); } return this->m_SelectedVertex != nullptr; } bool mitk::ContourModel::SelectVertexAt(int index, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep) && index >= 0) { return (this->m_SelectedVertex = this->m_ContourSeries[timestep]->GetVertexAt(index)); } return false; } bool mitk::ContourModel::SetControlVertexAt(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { VertexType *vertex = this->m_ContourSeries[timestep]->GetVertexAt(point, eps); if (vertex != nullptr) { vertex->IsControlPoint = true; return true; } } return false; } bool mitk::ContourModel::SetControlVertexAt(int index, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep) && index >= 0) { VertexType *vertex = this->m_ContourSeries[timestep]->GetVertexAt(index); if (vertex != nullptr) { vertex->IsControlPoint = true; return true; } } return false; } bool mitk::ContourModel::RemoveVertex(const VertexType *vertex, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (this->m_ContourSeries[timestep]->RemoveVertex(vertex)) { this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelSizeChangeEvent()); return true; } } return false; } bool mitk::ContourModel::RemoveVertexAt(int index, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (this->m_ContourSeries[timestep]->RemoveVertexAt(index)) { this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelSizeChangeEvent()); return true; } } return false; } bool mitk::ContourModel::RemoveVertexAt(Point3D &point, float eps, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { if (this->m_ContourSeries[timestep]->RemoveVertexAt(point, eps)) { this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelSizeChangeEvent()); return true; } } return false; } void mitk::ContourModel::ShiftSelectedVertex(Vector3D &translate) { if (this->m_SelectedVertex) { this->ShiftVertex(this->m_SelectedVertex, translate); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::ShiftContour(Vector3D &translate, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { // shift all vertices for (auto vertex : *(this->m_ContourSeries[timestep])) { this->ShiftVertex(vertex, translate); } this->Modified(); this->m_UpdateBoundingBox = true; this->InvokeEvent(ContourModelShiftEvent()); } } void mitk::ContourModel::ShiftVertex(VertexType *vertex, Vector3D &vector) { vertex->Coordinates[0] += vector[0]; vertex->Coordinates[1] += vector[1]; vertex->Coordinates[2] += vector[2]; } void mitk::ContourModel::Clear(TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { // clear data at timestep this->m_ContourSeries[timestep]->Clear(); this->Modified(); this->m_UpdateBoundingBox = true; } } void mitk::ContourModel::Expand(unsigned int timeSteps) { std::size_t oldSize = this->m_ContourSeries.size(); if (static_cast(timeSteps) > oldSize) { Superclass::Expand(timeSteps); // insert contours for each new timestep for (std::size_t i = oldSize; i < static_cast(timeSteps); i++) { m_ContourSeries.push_back(ContourElement::New()); } this->InvokeEvent(ContourModelExpandTimeBoundsEvent()); } } void mitk::ContourModel::SetRequestedRegionToLargestPossibleRegion() { // no support for regions } bool mitk::ContourModel::RequestedRegionIsOutsideOfTheBufferedRegion() { // no support for regions return false; } bool mitk::ContourModel::VerifyRequestedRegion() { // no support for regions return true; } void mitk::ContourModel::SetRequestedRegion(const itk::DataObject * /*data*/) { // no support for regions } void mitk::ContourModel::Clear() { // clear data and set to initial state again this->ClearData(); this->InitializeEmpty(); this->Modified(); this->m_UpdateBoundingBox = true; } void mitk::ContourModel::RedistributeControlVertices(int period, TimeStepType timestep) { if (!this->IsEmptyTimeStep(timestep)) { this->m_ContourSeries[timestep]->RedistributeControlVertices(this->GetSelectedVertex(), period); this->InvokeEvent(ContourModelClosedEvent()); this->Modified(); this->m_UpdateBoundingBox = true; } } mitk::ContourModel::VertexListType mitk::ContourModel::GetControlVertices(TimeStepType timestep) { VertexListType controlVertices; if (!this->IsEmptyTimeStep(timestep)) { controlVertices = this->m_ContourSeries[timestep]->GetControlVertices(); } return controlVertices; } mitk::ContourModel::VertexListType mitk::ContourModel::GetVertexList(TimeStepType timestep) { VertexListType controlVertices; if (!this->IsEmptyTimeStep(timestep)) { controlVertices = *this->m_ContourSeries[timestep]->GetVertexList(); } return controlVertices; } void mitk::ContourModel::ClearData() { // call the superclass, this releases the data of BaseData Superclass::ClearData(); // clear out the time resolved contours this->m_ContourSeries.clear(); } void mitk::ContourModel::Initialize() { this->InitializeEmpty(); this->Modified(); this->m_UpdateBoundingBox = true; } void mitk::ContourModel::Initialize(const ContourModel &other) { TimeStepType numberOfTimesteps = other.GetTimeGeometry()->CountTimeSteps(); this->InitializeTimeGeometry(numberOfTimesteps); for (TimeStepType currentTimestep = 0; currentTimestep < numberOfTimesteps; currentTimestep++) { this->m_ContourSeries.push_back(ContourElement::New()); this->SetClosed(other.IsClosed(currentTimestep), currentTimestep); } m_SelectedVertex = nullptr; this->m_lineInterpolation = other.m_lineInterpolation; this->Modified(); this->m_UpdateBoundingBox = true; } void mitk::ContourModel::InitializeEmpty() { // clear data at timesteps this->m_ContourSeries.resize(0); this->m_ContourSeries.push_back(ContourElement::New()); // set number of timesteps to one this->InitializeTimeGeometry(1); m_SelectedVertex = nullptr; this->m_lineInterpolation = ContourModel::LINEAR; } void mitk::ContourModel::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } if (this->m_UpdateBoundingBox) { // update the bounds of the geometry according to the stored vertices ScalarType mitkBounds[6]; // calculate the boundingbox at each timestep typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::PointsContainer PointsContainer; int timesteps = this->GetTimeSteps(); // iterate over the timesteps for (int currenTimeStep = 0; currenTimeStep < timesteps; currenTimeStep++) { if (dynamic_cast(this->GetGeometry(currenTimeStep))) { // do not update bounds for 2D geometries, as they are unfortunately defined with min bounds 0! return; } else { // we have a 3D geometry -> let's update bounds // only update bounds if the contour was modified if (this->GetMTime() > this->GetGeometry(currenTimeStep)->GetBoundingBox()->GetMTime()) { mitkBounds[0] = 0.0; mitkBounds[1] = 0.0; mitkBounds[2] = 0.0; mitkBounds[3] = 0.0; mitkBounds[4] = 0.0; mitkBounds[5] = 0.0; BoundingBoxType::Pointer boundingBox = BoundingBoxType::New(); PointsContainer::Pointer points = PointsContainer::New(); auto it = this->IteratorBegin(currenTimeStep); auto end = this->IteratorEnd(currenTimeStep); // fill the boundingbox with the points while (it != end) { Point3D currentP = (*it)->Coordinates; BoundingBoxType::PointType p; p.CastFrom(currentP); points->InsertElement(points->Size(), p); it++; } // construct the new boundingBox boundingBox->SetPoints(points); boundingBox->ComputeBoundingBox(); BoundingBoxType::BoundsArrayType tmp = boundingBox->GetBounds(); mitkBounds[0] = tmp[0]; mitkBounds[1] = tmp[1]; mitkBounds[2] = tmp[2]; mitkBounds[3] = tmp[3]; mitkBounds[4] = tmp[4]; mitkBounds[5] = tmp[5]; // set boundingBox at current timestep BaseGeometry *geometry3d = this->GetGeometry(currenTimeStep); geometry3d->SetBounds(mitkBounds); } } } this->m_UpdateBoundingBox = false; } GetTimeGeometry()->Update(); } void mitk::ContourModel::ExecuteOperation(Operation * /*operation*/) { // not supported yet } diff --git a/Modules/ContourModel/DataManagement/mitkContourModel.h b/Modules/ContourModel/DataManagement/mitkContourModel.h index f42452d056..f88e13b358 100644 --- a/Modules/ContourModel/DataManagement/mitkContourModel.h +++ b/Modules/ContourModel/DataManagement/mitkContourModel.h @@ -1,467 +1,467 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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_H_ #define _MITK_CONTOURMODEL_H_ #include "mitkBaseData.h" #include "mitkCommon.h" #include #include namespace mitk { /** \brief ContourModel is a structure of linked vertices defining a contour in 3D space. The vertices are stored in a mitk::ContourElement is stored for each timestep. The contour line segments are implicitly defined by the given linked vertices. By default two control points are are linked by a straight line.It is possible to add vertices at front and end of the contour and to iterate in both directions. Points are specified containing coordinates and additional (data) information, see mitk::ContourElement. For accessing a specific vertex either an index or a position in 3D Space can be used. The vertices are best accessed by using a VertexIterator. Interaction with the contour is thus available without any mitk interactor class using the api of ContourModel. It is possible to shift single vertices also as shifting the whole contour. A contour can be either open like a single curved line segment or closed. A closed contour can for example represent a jordan curve. \section mitkContourModelDisplayOptions Display Options The default mappers for this data structure are mitk::ContourModelGLMapper2D and mitk::ContourModelMapper3D. See these classes for display options which can can be set via properties. */ class MITKCONTOURMODEL_EXPORT ContourModel : public BaseData { public: mitkClassMacro(ContourModel, BaseData); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /*+++++++++++++++ typedefs +++++++++++++++++++++++++++++++*/ typedef ContourElement::VertexType VertexType; typedef ContourElement::VertexListType VertexListType; typedef ContourElement::VertexIterator VertexIterator; typedef ContourElement::ConstVertexIterator ConstVertexIterator; typedef std::vector ContourModelSeries; /*+++++++++++++++ END typedefs ++++++++++++++++++++++++++++*/ /** \brief Possible interpolation of the line segments between control points */ enum LineSegmentInterpolation { LINEAR, B_SPLINE }; /*++++++++++++++++ inline methods +++++++++++++++++++++++*/ /** \brief Get the current selected vertex. */ VertexType *GetSelectedVertex() { return this->m_SelectedVertex; } /** \brief Deselect vertex. */ void Deselect() { this->m_SelectedVertex = nullptr; } /** \brief Set selected vertex as control point */ void SetSelectedVertexAsControlPoint(bool isControlPoint = true) { if (this->m_SelectedVertex) { m_SelectedVertex->IsControlPoint = isControlPoint; this->Modified(); } } /** \brief Set the interpolation of the line segments between control points. */ void SetLineSegmentInterpolation(LineSegmentInterpolation interpolation) { this->m_lineInterpolation = interpolation; this->Modified(); } /** \brief Get the interpolation of the line segments between control points. */ LineSegmentInterpolation GetLineSegmentInterpolation() { return this->m_lineInterpolation; } /*++++++++++++++++ END inline methods +++++++++++++++++++++++*/ /** \brief Add a vertex to the contour at given timestep. The vertex is added at the end of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertex(const Point3D &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour at given timestep. A copy of the passed vertex is added at the end of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertex(const VertexType &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) \param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points will be rendered). @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertex(const Point3D& vertex, bool isControlPoint, TimeStepType timestep = 0); /** Clears the contour of destinationTimeStep and copies the contour of the passed source model at the sourceTimeStep. @pre soureModel must point to a valid instance @pre sourceTimePoint must be valid @note Updateing a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void UpdateContour(const ContourModel* sourceModel, TimeStepType destinationTimeStep, TimeStepType sourceTimeStep); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. The vertex is added at the FRONT of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertexAtFront(const Point3D &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. The vertex is added at the FRONT of contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertexAtFront(const VertexType &vertex, TimeStepType timestep = 0); /** \brief Add a vertex to the contour at given timestep AT THE FRONT of the contour. \param vertex - coordinate representation of a control point \param timestep - the timestep at which the vertex will be add ( default 0) \param isControlPoint - specifies the vertex to be handled in a special way (e.g. control points will be rendered). @note Adding a vertex to a timestep which exceeds the timebounds of the contour will not be added, the TimeGeometry will not be expanded. */ void AddVertexAtFront(const Point3D &vertex, bool isControlPoint, TimeStepType timestep = 0); /** \brief Insert a vertex at given index. */ void InsertVertexAtIndex(const Point3D &vertex, int index, bool isControlPoint = false, TimeStepType timestep = 0); /** \brief Set a coordinates for point at given index. */ bool SetVertexAt(int pointId, const Point3D &point, TimeStepType timestep = 0); /** \brief Set a coordinates and control state for point at given index. */ bool SetVertexAt(int pointId, const VertexType *vertex, TimeStepType timestep = 0); /** \brief Return if the contour is closed or not. */ bool IsClosed(int timestep = 0) const; /** \brief Concatenate two contours. The starting control point of the other will be added at the end of the contour. \param other \param timestep - the timestep at which the vertex will be add ( default 0) \param check - check for intersections ( default false) */ void Concatenate(ContourModel *other, TimeStepType timestep = 0, bool check = false); /** \brief Returns a const VertexIterator at the start element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator Begin(TimeStepType timestep = 0) const; /** \brief Returns a const VertexIterator at the start element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator IteratorBegin(TimeStepType timestep = 0) const; /** \brief Returns a const VertexIterator at the end element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator End(TimeStepType timestep = 0) const; /** \brief Returns a const VertexIterator at the end element of the contour. @throw mitk::Exception if the timestep is invalid. */ VertexIterator IteratorEnd(TimeStepType timestep = 0) const; /** \brief Close the contour. The last control point will be linked with the first point. */ virtual void Close(TimeStepType timestep = 0); /** \brief Set isClosed to false contour. The link between the last control point the first point will be removed. */ virtual void Open(TimeStepType timestep = 0); /** \brief Set closed property to given boolean. false - The link between the last control point the first point will be removed. true - The last control point will be linked with the first point. */ virtual void SetClosed(bool isClosed, TimeStepType timestep = 0); /** \brief Returns the number of vertices at a given timestep. \param timestep - default = 0 */ int GetNumberOfVertices(TimeStepType timestep = 0) const; /** \brief Returns whether the contour model is empty at a given timestep. \param timestep - default = 0 */ virtual bool IsEmpty(TimeStepType timestep) const; /** \brief Returns whether the contour model is empty. */ bool IsEmpty() const override; /** \brief Returns the vertex at the index position within the container. * If the index or timestep is invalid a nullptr will be returned. */ virtual const VertexType *GetVertexAt(int index, TimeStepType timestep = 0) const; /** Returns the next control vertex to the approximate nearest vertex of a given position in 3D space * If the timestep is invalid a nullptr will be returned. */ virtual const VertexType *GetNextControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const; /** Returns the previous control vertex to the approximate nearest vertex of a given position in 3D space * If the timestep is invalid a nullptr will be returned. */ virtual const VertexType *GetPreviousControlVertexAt(mitk::Point3D &point, float eps, TimeStepType timestep) const; /** \brief Remove a vertex at given timestep within the container. \return index of vertex. -1 if not found. */ int GetIndex(const VertexType *vertex, TimeStepType timestep = 0); /** \brief Check if there isn't something at this timestep. */ bool IsEmptyTimeStep(unsigned int t) const override; /** \brief Check if mouse cursor is near the contour. */ virtual bool IsNearContour(Point3D &point, float eps, TimeStepType timestep); /** \brief Mark a vertex at an index in the container as selected. */ bool SelectVertexAt(int index, TimeStepType timestep = 0); /** \brief Mark a vertex at an index in the container as control point. */ bool SetControlVertexAt(int index, TimeStepType timestep = 0); /** \brief Mark a control vertex at a given position in 3D space. \param point - query point in 3D space \param eps - radius for nearest neighbour search (error bound). \param timestep - search at this timestep @return true = vertex found; false = no vertex found */ bool SelectControlVertexAt(Point3D &point, float eps, TimeStepType timestep = 0); /** \brief Mark a vertex at a given position in 3D space. \param point - query point in 3D space \param eps - radius for nearest neighbour search (error bound). \param timestep - search at this timestep @return true = vertex found; false = no vertex found */ bool SelectVertexAt(Point3D &point, float eps, TimeStepType timestep = 0); /* \pararm point - query point in 3D space \pararm eps - radius for nearest neighbour search (error bound). \pararm timestep - search at this timestep @return true = vertex found; false = no vertex found */ bool SetControlVertexAt(Point3D &point, float eps, TimeStepType timestep = 0); /** \brief Remove a vertex at given index within the container. @return true = the vertex was successfuly removed; false = wrong index. */ bool RemoveVertexAt(int index, TimeStepType timestep = 0); /** \brief Remove a vertex at given timestep within the container. @return true = the vertex was successfuly removed. */ bool RemoveVertex(const VertexType *vertex, TimeStepType timestep = 0); /** \brief Remove a vertex at a query position in 3D space. The vertex to be removed will be search by nearest neighbour search. Note that possibly no vertex at this position and eps is stored inside the contour. @return true = the vertex was successfuly removed; false = no vertex found. */ bool RemoveVertexAt(Point3D &point, float eps, TimeStepType timestep = 0); /** \brief Shift the currently selected vertex by a translation vector. \param translate - the translation vector. */ void ShiftSelectedVertex(Vector3D &translate); /** \brief Shift the whole contour by a translation vector at given timestep. \param translate - the translation vector. \param timestep - at this timestep the contour will be shifted. */ void ShiftContour(Vector3D &translate, TimeStepType timestep = 0); /** \brief Clear the storage container at given timestep. All control points are removed at timestep. */ virtual void Clear(TimeStepType timestep); /** \brief Initialize all data objects */ void Initialize() override; /** \brief Initialize object with specs of other contour. Note: No data will be copied. */ void Initialize(const ContourModel &other); /** \brief Returns a list pointing to all vertices that are indicated to be control points. */ VertexListType GetControlVertices(TimeStepType timestep); /** \brief Returns the container of the vertices. */ VertexListType GetVertexList(TimeStepType timestep); /*++++++++++++++++++ method inherit from base data +++++++++++++++++++++++++++*/ /** \brief Inherit from base data - no region support available for contourModel objects. */ void SetRequestedRegionToLargestPossibleRegion() override; /** \brief Inherit from base data - no region support available for contourModel objects. */ bool RequestedRegionIsOutsideOfTheBufferedRegion() override; /** \brief Inherit from base data - no region support available for contourModel objects. */ bool VerifyRequestedRegion() override; /** \brief Inherit from base data - no region support available for contourModel objects. */ void SetRequestedRegion(const itk::DataObject *data) override; /** \brief Expand the contour model and its TimeGeometry to given number of timesteps. */ void Expand(unsigned int timeSteps) override; /** \brief Update the OutputInformation of a ContourModel object The BoundingBox of the contour will be updated, if necessary. */ void UpdateOutputInformation() override; /** \brief Clear the storage container. The object is set to initial state. All control points are removed and the number of timesteps are set to 1. */ void Clear() override; /** \brief overwrite if the Data can be called by an Interactor (StateMachine). */ void ExecuteOperation(Operation *operation) override; /** \brief Redistributes ontrol vertices with a given period (as number of vertices) \param period - the number of vertices between control points. \param timestep - at this timestep all lines will be rebuilt. */ virtual void RedistributeControlVertices(int period, TimeStepType timestep); protected: mitkCloneMacro(Self); ContourModel(); ContourModel(const ContourModel &other); ~ContourModel() override; // inherit from BaseData. called by Clear() void ClearData() override; // inherit from BaseData. Initial state of a contour with no vertices and a single timestep. void InitializeEmpty() override; // Shift a vertex static void ShiftVertex(VertexType *vertex, Vector3D &vector); // Storage with time resolved support. ContourModelSeries m_ContourSeries; // The currently selected vertex. VertexType *m_SelectedVertex; // The interpolation of the line segment between control points. LineSegmentInterpolation m_lineInterpolation; // only update the bounding geometry if necessary bool m_UpdateBoundingBox; }; - itkEventMacro(ContourModelEvent, itk::AnyEvent); - itkEventMacro(ContourModelShiftEvent, ContourModelEvent); - itkEventMacro(ContourModelSizeChangeEvent, ContourModelEvent); - itkEventMacro(ContourModelAddEvent, ContourModelSizeChangeEvent); - itkEventMacro(ContourModelRemoveEvent, ContourModelSizeChangeEvent); - itkEventMacro(ContourModelExpandTimeBoundsEvent, ContourModelEvent); - itkEventMacro(ContourModelClosedEvent, ContourModelEvent); + itkEventMacroDeclaration(ContourModelEvent, itk::AnyEvent); + itkEventMacroDeclaration(ContourModelShiftEvent, ContourModelEvent); + itkEventMacroDeclaration(ContourModelSizeChangeEvent, ContourModelEvent); + itkEventMacroDeclaration(ContourModelAddEvent, ContourModelSizeChangeEvent); + itkEventMacroDeclaration(ContourModelRemoveEvent, ContourModelSizeChangeEvent); + itkEventMacroDeclaration(ContourModelExpandTimeBoundsEvent, ContourModelEvent); + itkEventMacroDeclaration(ContourModelClosedEvent, ContourModelEvent); } #endif diff --git a/Modules/ContourModel/IO/mitkContourModelWriter.h b/Modules/ContourModel/IO/mitkContourModelWriter.h index 0add146a3b..639fec3e2d 100644 --- a/Modules/ContourModel/IO/mitkContourModelWriter.h +++ b/Modules/ContourModel/IO/mitkContourModelWriter.h @@ -1,158 +1,160 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _MITK_CONTOURMODEL_WRITER__H_ #define _MITK_CONTOURMODEL_WRITER__H_ #include #include // DEPRECATED #include namespace mitk { /** * @brief XML-based writer for mitk::ContourModels * * XML-based writer for mitk::ContourModels. Multiple ContourModels can be written in * a single XML file by simply setting multiple inputs to the filter. * * The xml file will look like: * + * \code{.unparsed} * * * * * * * * * * * * * * * * * * + * \endcode * * @ingroup MitkContourModelModule */ class ContourModelWriter : public mitk::AbstractFileWriter { public: explicit ContourModelWriter(bool writeXMLHeader = true); ~ContourModelWriter() override; using AbstractFileWriter::Write; void Write() override; protected: ContourModelWriter(const ContourModelWriter &other); mitk::ContourModelWriter *Clone() const override; /** * Converts an arbitrary type to a string. The type has to * support the << operator. This works fine at least for integral * data types as float, int, long etc. * @param value the value to convert * @returns the string representation of value */ template std::string ConvertToString(T value); /** * Writes an XML representation of the given point set to * an outstream. The XML-Header an root node is not included! * @param contourModel the point set to be converted to xml * @param out the stream to write to. */ void WriteXML(const mitk::ContourModel *contourModel, std::ostream &out); /** * Writes the geometry information of the TimeGeometry to an outstream. * The root tag is not included. * @param geometry the TimeGeometry of the contour. * @param out the stream to write to. */ void WriteGeometryInformation(const mitk::TimeGeometry *geometry, std::ostream &out); /** * Writes an standard xml header to the given stream. * @param file the stream in which the header is written. */ void WriteXMLHeader(std::ostream &file); /** Write a start element tag */ void WriteStartElement(const char *const tag, std::ostream &file); void WriteStartElementWithAttribut(const char *const tag, std::vector attributes, std::vector values, std::ostream &file); /** * Write an end element tag * End-Elements following character data should pass indent = false. */ void WriteEndElement(const char *const tag, std::ostream &file, const bool &indent = true); /** Write character data inside a tag. */ void WriteCharacterData(const char *const data, std::ostream &file); /** Write a start element tag */ void WriteStartElement(std::string &tag, std::ostream &file); /** Write an end element tag */ void WriteEndElement(std::string &tag, std::ostream &file, const bool &indent = true); /** Write character data inside a tag. */ void WriteCharacterData(std::string &data, std::ostream &file); /** Writes empty spaces to the stream according to m_IndentDepth and m_Indent */ void WriteIndent(std::ostream &file); bool m_WriteXMLHeader; unsigned int m_IndentDepth; unsigned int m_Indent; public: static const char *XML_CONTOURMODEL; static const char *XML_HEAD; static const char *XML_GEOMETRY_INFO; static const char *XML_DATA; static const char *XML_TIME_STEP; static const char *XML_CONTROL_POINTS; static const char *XML_POINT; static const char *XML_X; static const char *XML_Y; static const char *XML_Z; }; } #endif diff --git a/Modules/Core/CMakeLists.txt b/Modules/Core/CMakeLists.txt index b38af61df6..27d967637d 100644 --- a/Modules/Core/CMakeLists.txt +++ b/Modules/Core/CMakeLists.txt @@ -1,77 +1,77 @@ set(TOOL_CPPS "") # temporary suppress warnings in the following files until image accessors are fully integrated. set_source_files_properties( src/DataManagement/mitkImage.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) set_source_files_properties( src/Controllers/mitkSliceNavigationController.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) #if(MSVC) # set(optional_private_package_depends psapi) #endif() mitk_create_module( INCLUDE_DIRS PUBLIC ${MITK_BINARY_DIR} PRIVATE src/Algorithms src/Controllers src/DataManagement src/Interactions src/IO src/Rendering DEPENDS PUBLIC mbilog CppMicroServices PACKAGE_DEPENDS PUBLIC Boost ITK|IOImageBase+SpatialObjects+Statistics #ITK|Statistics+Transform VTK|FiltersTexture+FiltersParallel+ImagingStencil+ImagingMath+InteractionStyle+RenderingOpenGL2+RenderingVolumeOpenGL2+RenderingFreeType+RenderingLabel+InteractionWidgets+IOGeometry+IOXML PRIVATE - ITK|IOBioRad+IOBMP+IOBruker+IOCSV+IOGDCM+IOGE+IOGIPL+IOHDF5+IOIPL+IOJPEG+IOLSM+IOMesh+IOMeta+IOMINC+IOMRC+IONIFTI+IONRRD+IOPNG+IOSiemens+IOSpatialObjects+IOStimulate+IOTIFF+IOTransformBase+IOTransformHDF5+IOTransformInsightLegacy+IOTransformMatlab+IOVTK+IOXML + ITK|IOBioRad+IOBMP+IOBruker+IOCSV+IOGDCM+IOGE+IOGIPL+IOHDF5+IOIPL+IOJPEG+IOJPEG2000+IOLSM+IOMesh+IOMeta+IOMINC+IOMRC+IONIFTI+IONRRD+IOPNG+IOSiemens+IOSpatialObjects+IOStimulate+IOTIFF+IOTransformBase+IOTransformHDF5+IOTransformInsightLegacy+IOTransformMatlab+IOVTK+IOXML tinyxml2 ${optional_private_package_depends} # Do not automatically create CppMicroServices initialization code. # Because the VTK "auto-init" functionality injects file-local static # initialization code in every cpp file which includes a VTK header, # static initialization order becomes an issue again. For the Mitk # core library, we need to ensure that the VTK static initialization stuff # happens before the CppMicroServices initialization, since the latter # might already use VTK code which needs to access VTK object factories. # Hence, CppMicroServices initialization code is placed manually within # the mitkCoreActivator.cpp file. NO_INIT ) if(NOT TARGET ${MODULE_TARGET}) message(SEND_ERROR "Core target ${MODULE_TARGET} does not exist") endif() function(_itk_create_factory_register_manager) # In MITK_ITK_Config.cmake, we do *not* include ITK_USE_FILE, which # prevents multiple registrations/unregistrations of ITK IO factories # during library loading/unloading (of MITK libraries). However, we need # "one" place where the IO factories are registered at # least once. This could be the application executable, but every executable would # need to take care of that itself. Instead, we allow the auto registration in the # Mitk Core library. set(NO_DIRECTORY_SCOPED_ITK_COMPILE_DEFINITION 1) find_package(ITK) include(${ITK_USE_FILE}) if(NOT ITK_NO_IO_FACTORY_REGISTER_MANAGER) # We manually add the define which will be of target scope. MITK # patches ITK_USE_FILE to remove the directory scoped compile # definition since it would be propagated to other targets in the # same directory scope but these targets might want to *not* # use the ITK factory manager stuff. target_compile_definitions(${MODULE_TARGET} PRIVATE ITK_IO_FACTORY_REGISTER_MANAGER) endif() endfunction() _itk_create_factory_register_manager() if(BUILD_TESTING) add_subdirectory(TestingHelper) add_subdirectory(test) endif() diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index fd48d9ca2c..3637647f25 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,326 +1,328 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCoreActivator.cpp mitkCoreObjectFactoryBase.cpp mitkCoreObjectFactory.cpp mitkCoreServices.cpp mitkException.cpp Algorithms/mitkBaseDataSource.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkCompositePixelValueToString.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkExtractSliceFilter2.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkTemporalJoinImagesFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkLayerController.cpp + DataManagement/mitkAnatomicalStructureColorPresets.cpp DataManagement/mitkArbitraryTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkGenericIDRelationRule.cpp DataManagement/mitkIdentifiable.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyOwner.cpp DataManagement/mitkIPropertyPersistence.cpp DataManagement/mitkIPropertyProvider.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDataUID.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateFunction.cpp DataManagement/mitkNodePredicateGeometry.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateDataProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkNodePredicateSubGeometry.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyKeyPath.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyNameHelper.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkPropertyPersistence.cpp DataManagement/mitkPropertyPersistenceInfo.cpp DataManagement/mitkPropertyRelationRuleBase.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkScaleOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkSourceImageRelationRule.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkUIDManipulator.cpp DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkIPropertyRelations.cpp DataManagement/mitkPropertyRelations.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayActionEventBroadcast.cpp Interactions/mitkDisplayActionEventFunctions.cpp Interactions/mitkDisplayActionEventHandler.cpp Interactions/mitkDisplayActionEventHandlerDesynchronized.cpp Interactions/mitkDisplayActionEventHandlerStd.cpp Interactions/mitkDisplayActionEventHandlerSynchronized.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionSchemeSwitcher.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp IO/mitkLocaleSwitch.cpp IO/mitkLog.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkProportionalTimeGeometryToXML.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp + IO/mitkUtf8Util.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkPreferenceListReaderOptionsFunctor.cpp IO/mitkIOMetaInformationPropertyConstants.cpp Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkMapper.cpp Rendering/mitkAnnotation.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp #Rendering/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigMITKBase.xml Interactions/DisplayConfigPACSBase.xml Interactions/DisplayConfigCrosshair.xml Interactions/DisplayConfigRotation.xml Interactions/DisplayConfigActivateCoupling.xml Interactions/DisplayConfigSwivel.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITKLimited.xml Interactions/DisplayConfigBlockLMB.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml mitkAnatomicalStructureColorPresets.xml ) diff --git a/Modules/Core/include/itkImportMitkImageContainer.h b/Modules/Core/include/itkImportMitkImageContainer.h index 59f79e4ab4..1343566f22 100644 --- a/Modules/Core/include/itkImportMitkImageContainer.h +++ b/Modules/Core/include/itkImportMitkImageContainer.h @@ -1,102 +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 __itkImportMitkImageContainer_h #define __itkImportMitkImageContainer_h #include #include #include namespace itk { /** \class ImportMitkImageContainer * Defines an itk::Image front-end to an mitk::Image. This container * conforms to the ImageContainerInterface. This is a full-fleged Object, * so there is modification time, debug, and reference count information. * * Template parameters for ImportMitkImageContainer: * * TElementIdentifier = * An INTEGRAL type for use in indexing the imported buffer. * * TElement = * The element type stored in the container. */ template class ImportMitkImageContainer : public ImportImageContainer { public: /** Standard class typedefs. */ typedef ImportMitkImageContainer Self; typedef Object Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Save the template parameters. */ typedef TElementIdentifier ElementIdentifier; typedef TElement Element; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** Standard part of every itk Object. */ itkTypeMacro(ImportMitkImageContainer, ImportImageContainer); ///** Get the pointer from which the image data is imported. */ // TElement *GetImportPointer() {return m_ImportPointer;}; /** \brief Set the mitk::ImageDataItem to be imported */ // void SetImageDataItem(mitk::ImageDataItem* imageDataItem); void SetImageAccessor(mitk::ImageAccessorBase *imageAccess, size_t noBytes); protected: ImportMitkImageContainer(); ~ImportMitkImageContainer() override; /** PrintSelf routine. Normally this is a protected internal method. It is * made public here so that Image can call this method. Users should not * call this method but should call Print() instead. */ void PrintSelf(std::ostream &os, Indent indent) const override; private: ImportMitkImageContainer(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented // mitk::ImageDataItem::Pointer m_ImageDataItem; mitk::ImageAccessorBase *m_imageAccess; }; } // end namespace itk // Define instantiation macro for this template. #define ITK_TEMPLATE_ImportMitkImageContainer(_, EXPORT, x, y) \ namespace itk \ { \ _(2(class EXPORT ImportMitkImageContainer)) \ namespace Templates \ { \ typedef ImportMitkImageContainer ImportMitkImageContainer##y; \ } \ } -//#if ITK_TEMPLATE_EXPLICIT -//# include "Templates/itkImportMitkImageContainer+-.h" -//#endif - -#if ITK_TEMPLATE_TXX +#ifndef ITK_MANUAL_INSTANTIATION #include "itkImportMitkImageContainer.txx" #endif #endif diff --git a/Modules/Core/include/itkVtkAbstractTransform.h b/Modules/Core/include/itkVtkAbstractTransform.h index 54ff4715b7..3a241fe832 100644 --- a/Modules/Core/include/itkVtkAbstractTransform.h +++ b/Modules/Core/include/itkVtkAbstractTransform.h @@ -1,101 +1,102 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKVTKABSTRACTTRANSFORM_H_HEADER_INCLUDED_C1C68A2C #define MITKVTKABSTRACTTRANSFORM_H_HEADER_INCLUDED_C1C68A2C #include "itkTransform.h" #include class vtkAbstractTransform; namespace itk { //##Documentation //## @brief Adapter from vtkAbstractTransform to itk::Transform //## @ingroup Geometry template class VtkAbstractTransform : public itk::Transform { public: typedef VtkAbstractTransform Self; typedef Transform Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef typename Superclass::OutputPointType OutputPointType; typedef typename Superclass::OutputVectorType OutputVectorType; typedef typename Superclass::OutputVnlVectorType OutputVnlVectorType; typedef typename Superclass::OutputCovariantVectorType OutputCovariantVectorType; typedef typename Superclass::InputPointType InputPointType; typedef typename Superclass::InputVectorType InputVectorType; typedef typename Superclass::InputVnlVectorType InputVnlVectorType; typedef typename Superclass::InputCovariantVectorType InputCovariantVectorType; typedef typename Superclass::ParametersType ParametersType; typedef typename Superclass::JacobianType JacobianType; + typedef typename Superclass::JacobianPositionType JacobianPositionType; itkFactorylessNewMacro(Self); itkCloneMacro(Self); //##Documentation //## @brief Get the vtkAbstractTransform (stored in m_VtkAbstractTransform) virtual vtkAbstractTransform *GetVtkAbstractTransform() const; //##Documentation //## @brief Get the inverse vtkAbstractTransform (stored in m_InverseVtkAbstractTransform) virtual vtkAbstractTransform *GetInverseVtkAbstractTransform() const; //##Documentation //## @brief Set the vtkAbstractTransform (stored in m_VtkAbstractTransform) virtual void SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform); using Superclass::TransformVector; using Superclass::TransformCovariantVector; OutputPointType TransformPoint(const InputPointType &) const override; OutputVectorType TransformVector(const InputVectorType &) const override; OutputVnlVectorType TransformVector(const InputVnlVectorType &) const override; OutputCovariantVectorType TransformCovariantVector(const InputCovariantVectorType &) const override; virtual InputPointType BackTransform(const OutputPointType &point) const; virtual InputVectorType BackTransform(const OutputVectorType &vector) const; virtual InputVnlVectorType BackTransform(const OutputVnlVectorType &vector) const; virtual InputCovariantVectorType BackTransform(const OutputCovariantVectorType &vector) const; void SetParameters(const ParametersType &) override; void SetFixedParameters(const ParametersType &) override; void ComputeJacobianWithRespectToParameters(const InputPointType &, JacobianType &) const override; - void ComputeJacobianWithRespectToPosition(const InputPointType &, JacobianType &) const override; + void ComputeJacobianWithRespectToPosition(const InputPointType &, JacobianPositionType &) const override; - unsigned long GetMTime() const override; + itk::ModifiedTimeType GetMTime() const override; protected: VtkAbstractTransform(); ~VtkAbstractTransform() override; //##Documentation //## @brief Instance of the vtkAbstractTransform vtkAbstractTransform *m_VtkAbstractTransform; //##Documentation //## @brief Instance of the vtkAbstractTransform vtkAbstractTransform *m_InverseVtkAbstractTransform; mutable unsigned long m_LastVtkAbstractTransformTimeStamp; }; } // namespace itk #ifndef MITK_MANUAL_INSTANTIATION #include "itkVtkAbstractTransform.txx" #endif #endif /* MITKVTKABSTRACTTRANSFORM_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Modules/Core/include/itkVtkAbstractTransform.txx b/Modules/Core/include/itkVtkAbstractTransform.txx index f00836fc51..6e6a87ee5c 100644 --- a/Modules/Core/include/itkVtkAbstractTransform.txx +++ b/Modules/Core/include/itkVtkAbstractTransform.txx @@ -1,236 +1,236 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "itkVtkAbstractTransform.h" #include #include namespace itk { template itk::VtkAbstractTransform::VtkAbstractTransform() : m_VtkAbstractTransform(nullptr), m_InverseVtkAbstractTransform(nullptr), m_LastVtkAbstractTransformTimeStamp(0) { } template itk::VtkAbstractTransform::~VtkAbstractTransform() { if (m_VtkAbstractTransform != nullptr) m_VtkAbstractTransform->UnRegister(nullptr); } template vtkAbstractTransform *itk::VtkAbstractTransform::GetVtkAbstractTransform() const { return m_VtkAbstractTransform; } template vtkAbstractTransform *itk::VtkAbstractTransform::GetInverseVtkAbstractTransform() const { return m_InverseVtkAbstractTransform; } template void itk::VtkAbstractTransform::SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform) { if (m_VtkAbstractTransform == aVtkAbstractTransform) return; if (m_VtkAbstractTransform != nullptr) m_VtkAbstractTransform->UnRegister(nullptr); m_VtkAbstractTransform = aVtkAbstractTransform; if (m_VtkAbstractTransform != nullptr) { m_VtkAbstractTransform->Register(nullptr); m_InverseVtkAbstractTransform = m_VtkAbstractTransform->GetInverse(); // memory managed by m_VtkAbstractTransform } m_LastVtkAbstractTransformTimeStamp = m_VtkAbstractTransform->GetMTime(); this->Modified(); } // Transform a point template typename itk::VtkAbstractTransform::OutputPointType itk::VtkAbstractTransform::TransformPoint(const InputPointType &point) const { assert(m_VtkAbstractTransform != nullptr); OutputPointType outputpoint; vnl_vector vnl_vec; mitk::ScalarType vtkpt[3]; mitk::itk2vtk(point, vtkpt); m_VtkAbstractTransform->TransformPoint(vtkpt, vtkpt); mitk::vtk2itk(vtkpt, outputpoint); return outputpoint; } // Transform a vector template typename itk::VtkAbstractTransform::OutputVectorType itk::VtkAbstractTransform::TransformVector(const InputVectorType &vect) const { assert(m_VtkAbstractTransform != nullptr); OutputVectorType outputvector; vnl_vector vnl_vec; mitk::ScalarType vtkpt[3] = {0, 0, 0}; mitk::ScalarType vtkvec[3]; mitk::vnl2vtk(vect.GetVnlVector(), vtkvec); m_VtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Transform a vnl_vector_fixed template typename itk::VtkAbstractTransform::OutputVnlVectorType itk::VtkAbstractTransform::TransformVector(const InputVnlVectorType &vect) const { assert(m_VtkAbstractTransform != nullptr); OutputVnlVectorType outputvector; mitk::ScalarType vtkpt[3] = {0, 0, 0}; mitk::ScalarType vtkvec[3]; mitk::vnl2vtk(vect, vtkvec); m_VtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Transform a CovariantVector template typename itk::VtkAbstractTransform::OutputCovariantVectorType itk::VtkAbstractTransform::TransformCovariantVector(const InputCovariantVectorType & /*vec*/) const { itkExceptionMacro(<< "implement before using!"); OutputCovariantVectorType result; // Converted vector // for (unsigned int i = 0; i < NDimensions; i++) // { // result[i] = NumericTraits::Zero; // for (unsigned int j = 0; j < NDimensions; j++) // { // result[i] += m_Inverse[j][i]*vec[j]; // Inverse transposed // } // } return result; } // Back transform a point template typename VtkAbstractTransform::InputPointType itk::VtkAbstractTransform::BackTransform( const OutputPointType &point) const { assert(m_VtkAbstractTransform != nullptr); OutputPointType outputpoint; mitk::ScalarType vtkpt[3]; mitk::itk2vtk(point, vtkpt); m_InverseVtkAbstractTransform->TransformPoint(vtkpt, vtkpt); mitk::vtk2itk(vtkpt, outputpoint); return outputpoint; } // Back transform a vector template typename VtkAbstractTransform::InputVectorType itk::VtkAbstractTransform::BackTransform( const OutputVectorType &vect) const { assert(m_VtkAbstractTransform != nullptr); OutputVectorType outputvector; mitk::ScalarType vtkpt[3] = {0, 0, 0}; mitk::ScalarType vtkvec[3]; mitk::itk2vtk(vect, vtkvec); m_InverseVtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Back transform a vnl_vector template typename VtkAbstractTransform::InputVnlVectorType itk::VtkAbstractTransform::BackTransform( const OutputVnlVectorType &vect) const { assert(m_InverseVtkAbstractTransform != nullptr); OutputVnlVectorType outputvector; mitk::ScalarType vtkpt[3] = {0, 0, 0}; mitk::ScalarType vtkvec[3]; mitk::itk2vtk(vect, vtkvec); m_InverseVtkAbstractTransform->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::vtk2itk(vtkvec, outputvector); return outputvector; } // Back Transform a CovariantVector template typename VtkAbstractTransform::InputCovariantVectorType itk::VtkAbstractTransform::BackTransform(const OutputCovariantVectorType &vec) const { itkExceptionMacro(<< "implement before using!"); // for (unsigned int i = 0; i < NDimensions; i++) // { // result[i] = NumericTraits::Zero; // for (unsigned int j = 0; j < NDimensions; j++) // { // result[i] += m_Matrix[j][i]*vec[j]; // Direct matrix transposed // } // } return vec; } template - unsigned long itk::VtkAbstractTransform::GetMTime() const + itk::ModifiedTimeType itk::VtkAbstractTransform::GetMTime() const { if ((m_VtkAbstractTransform != nullptr) && (m_LastVtkAbstractTransformTimeStamp < m_VtkAbstractTransform->GetMTime())) { m_LastVtkAbstractTransformTimeStamp = m_VtkAbstractTransform->GetMTime(); this->Modified(); } return Superclass::GetMTime(); } template void itk::VtkAbstractTransform::SetParameters(const ParametersType &) { // TODO } template void itk::VtkAbstractTransform::SetFixedParameters(const ParametersType &) { // TODO } template void itk::VtkAbstractTransform::ComputeJacobianWithRespectToParameters(const InputPointType &, JacobianType &) const { // TODO } template void itk::VtkAbstractTransform::ComputeJacobianWithRespectToPosition(const InputPointType &, - JacobianType &) const + JacobianPositionType &) const { // TODO } } // namespace itk diff --git a/Modules/Core/include/mitkAbstractTransformGeometry.h b/Modules/Core/include/mitkAbstractTransformGeometry.h index 611fcf7524..17a706ec3a 100644 --- a/Modules/Core/include/mitkAbstractTransformGeometry.h +++ b/Modules/Core/include/mitkAbstractTransformGeometry.h @@ -1,234 +1,234 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #define MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #include "mitkPlaneGeometry.h" #include #include "itkVtkAbstractTransform.h" class vtkAbstractTransform; namespace mitk { //##Documentation //## @brief Describes a geometry defined by an vtkAbstractTransform and a plane //## //## vtkAbstractTransform is the most general transform in vtk (superclass for //## all vtk geometric transformations). It defines an arbitrary 3D transformation, //## i.e., a transformation of 3D space into 3D space. In contrast, //## AbstractTransformGeometry (since it is a subclass of PlaneGeometry) describes a //## 2D manifold in 3D space. The 2D manifold is defined as the manifold that results //## from transforming a rectangle (given in m_Plane as a PlaneGeometry) by the //## vtkAbstractTransform (given in m_VtkAbstractTransform). //## The PlaneGeometry m_Plane is used to define the parameter space. 2D coordinates are //## first mapped by the PlaneGeometry and the resulting 3D coordinates are put into //## the vtkAbstractTransform. //## @note This class is the superclass of concrete geometries. Since there is no //## write access to the vtkAbstractTransform and m_Plane, this class is somehow //## abstract. For full write access from extern, use ExternAbstractTransformGeometry. //## @note The bounds of the PlaneGeometry are used as the parametric bounds. //## @sa ExternAbstractTransformGeometry //## @ingroup Geometry class MITKCORE_EXPORT AbstractTransformGeometry : public PlaneGeometry { public: mitkClassMacro(AbstractTransformGeometry, PlaneGeometry); itkFactorylessNewMacro(Self); itkCloneMacro(Self); //##Documentation //## @brief Get the vtkAbstractTransform (stored in m_VtkAbstractTransform) virtual vtkAbstractTransform *GetVtkAbstractTransform() const; - unsigned long GetMTime() const override; + itk::ModifiedTimeType GetMTime() const override; //##Documentation //## @brief Get the rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## AbstractTransformGeometry itkGetConstObjectMacro(Plane, PlaneGeometry); /** * \brief projects the given point onto the curved plane */ bool Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const override; /** * \brief projects a given vector starting from given point onto the curved plane * \warning no satisfiyng implementation existing yet */ bool Project(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const override; /** * \brief projects a given vector starting from standard point onto the curved plane * \warning no satisfying implementation existing yet */ bool Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const override; bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const override; void Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const override; bool Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const override; void Map(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const override; void IndexToWorld(const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const override; void WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const override; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## @deprecated First parameter (Point2D) is not used. If possible, please use void IndexToWorld(const // mitk::Vector2D& vec_units, mitk::Vector2D& vec_mm) const. //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point2D &atPt2d_units, const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const override; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const override; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## @deprecated First parameter (Point2D) is not used. If possible, please use void WorldToIndex(const // mitk::Vector2D& vec_mm, mitk::Vector2D& vec_units) const. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const override; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const override; bool IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox = false) const override; virtual mitk::ScalarType GetParametricExtentInMM(int direction) const; virtual const itk::Transform *GetParametricTransform() const; //##Documentation //## @brief Change the parametric bounds to @a oversampling times //## the bounds of m_Plane. //## //## The change is done once (immediately). Later changes of the bounds //## of m_Plane will not influence the parametric bounds. (Consequently, //## there is no method to get the oversampling.) virtual void SetOversampling(mitk::ScalarType oversampling); //##Documentation //## @brief Calculates the standard part of a BaseGeometry //## (IndexToWorldTransform and bounding box) around the //## curved geometry. Has to be implemented in subclasses. //## //## \sa SetFrameGeometry virtual void CalculateFrameGeometry(); //##Documentation //## @brief Set the frame geometry which is used as the standard //## part of an BaseGeometry (IndexToWorldTransform and bounding box) //## //## Maybe used as a hint within which the interpolation shall occur //## by concrete sub-classes. //## \sa CalculateFrameGeometry virtual void SetFrameGeometry(const mitk::BaseGeometry *frameGeometry); itk::LightObject::Pointer InternalClone() const override; //##Documentation //## @brief Get the parametric bounding-box //## //## See AbstractTransformGeometry for an example usage of this. itkGetConstObjectMacro(ParametricBoundingBox, BoundingBox); //##Documentation //## @brief Get the parametric bounds //## //## See AbstractTransformGeometry for an example usage of this. const BoundingBox::BoundsArrayType &GetParametricBounds() const; //##Documentation //## @brief Get the parametric extent //## //## See AbstractTransformGeometry for an example usage of this. mitk::ScalarType GetParametricExtent(int direction) const; protected: AbstractTransformGeometry(); AbstractTransformGeometry(const AbstractTransformGeometry &other); ~AbstractTransformGeometry() override; //##Documentation //## @brief Set the vtkAbstractTransform (stored in m_VtkAbstractTransform) //## //## Protected in this class, made public in ExternAbstractTransformGeometry. virtual void SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform); //##Documentation //## @brief Set the rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## ExternAbstractTransformGeometry //## //## Protected in this class, made public in ExternAbstractTransformGeometry. //## @note The bounds of the PlaneGeometry are used as the parametric bounds. //## @note The PlaneGeometry is cloned, @em not linked/referenced. virtual void SetPlane(const mitk::PlaneGeometry *aPlane); //##Documentation //## @brief The rectangular area that is used for transformation by //## m_VtkAbstractTransform and therewith defines the 2D manifold described by //## AbstractTransformGeometry. mitk::PlaneGeometry::Pointer m_Plane; itk::VtkAbstractTransform::Pointer m_ItkVtkAbstractTransform; mitk::BaseGeometry::Pointer m_FrameGeometry; //##Documentation //## @brief Set the parametric bounds //## //## Protected in this class, made public in some sub-classes, e.g., //## ExternAbstractTransformGeometry. virtual void SetParametricBounds(const BoundingBox::BoundsArrayType &bounds); mutable mitk::BoundingBox::Pointer m_ParametricBoundingBox; //##Documentation //## @brief PreSetSpacing //## //## These virtual function allows a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use //## {Superclass::PreSetSpacing();}; void PreSetSpacing(const mitk::Vector3D &aSpacing) override { Superclass::PreSetSpacing(aSpacing); }; }; } // namespace mitk #endif /* MITKVTKABSTRACTTRANSFORMPLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Modules/Core/include/mitkAffineTransform3D.h b/Modules/Core/include/mitkAffineTransform3D.h index 8a12d47dd8..e6f535cd75 100644 --- a/Modules/Core/include/mitkAffineTransform3D.h +++ b/Modules/Core/include/mitkAffineTransform3D.h @@ -1,24 +1,24 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKAFFINETRANSFORM3D_H_ -#define MITKAFFINETRANSFORM3D_H_ +#ifndef mitkAffineTransform3D_h +#define mitkAffineTransform3D_h -#include -#include +#include +#include namespace mitk { - typedef itk::AffineGeometryFrame::TransformType AffineTransform3D; + using AffineTransform3D = itk::ScalableAffineTransform; } -#endif /* MITKAFFINETRANSFORM3D_H_ */ +#endif diff --git a/Modules/Core/include/mitkArray.h b/Modules/Core/include/mitkArray.h index 7709718692..c1b86bbd17 100644 --- a/Modules/Core/include/mitkArray.h +++ b/Modules/Core/include/mitkArray.h @@ -1,143 +1,142 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKARRAY_H_ #define MITKARRAY_H_ #include #include "mitkEqual.h" #include "mitkNumericConstants.h" namespace mitk { /** * Methods to copy from itk::FixedArray s (like mitk::Vector and mitk::Point) into ArrayTypes and vice versa. * ArrayTypes here are all types who implement operator[]. * The two templated methods were made free floating so you may specialize them * for your concrete type. */ /** * @brief Copies elements of an array to this Vector * @param[in] array the array whose values will be copied into the Vector. Must be of a type which overrides the [] * operator * @param[out] toArray the FixedArrray (e.g., mitk::Vector or mitk::Point) which should hold the elements of array. * @attention array must be of dimension NVectorDimension! * @attention this method implicitly converts between data types. */ template void FillArray(itk::FixedArray &toArray, const ArrayType &array) { - itk::FixedArray vectorOrPoint; for (unsigned short int var = 0; var < NVectorDimension; ++var) { toArray[var] = array[var]; } } /** * @brief Copies elements of an array to this Vector * @param[in] array the array whose values will be copied into the Vector. Must be of a type which overrides the [] * operator * @return the FixedArray (e.g., mitk::Vector or mitk::Point) which should hold the elements of array. * @attention array must be of dimension NVectorDimension! * @attention this method implicitly converts between data types. */ template itk::FixedArray FillArray(const ArrayType &array) { itk::FixedArray vectorOrPoint; mitk::FillArray(vectorOrPoint, array); return vectorOrPoint; } /** * @brief Copies the elements of this into an array * @param[in] vectorOrPoint the itk::FixedArray which shall be copied into the array. Can e.g. be of type * mitk::Vector * or mitk::Point * @param[out] array the array which will hold the elements. Must be of a type which overrides the [] operator. * @attention array must be of dimension NVectorDimension! * @attention this method implicitly converts between data types. */ template void ToArray(ArrayType &array, const itk::FixedArray &vectorOrPoint) { for (unsigned short int var = 0; var < NVectorDimension; ++var) { array[var] = vectorOrPoint[var]; } } /** * @brief Copies the elements of this into an array * @param[in] vectorOrPoint the itk::FixedArray which shall be copied into the array. Can e.g. be of type * mitk::Vector * or mitk::Point * @return the array which will hold the elements. Must be of a type which overrides the [] operator. * @attention array must be of dimension NVectorDimension! * @attention this method implicitly converts between data types. */ template ArrayType ToArray(const itk::FixedArray &vectorOrPoint) { ArrayType result; mitk::ToArray(result, vectorOrPoint); return result; } // The FillVector3D and FillVector4D methods are implemented for all common array types here template inline void FillVector3D(Tout &out, mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z) { out[0] = x; out[1] = y; out[2] = z; } template inline void FillVector4D(Tout &out, mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z, mitk::ScalarType t) { out[0] = x; out[1] = y; out[2] = z; out[3] = t; } /** * Compares two ArrayTypes of size size. * ArrayTypes are all Objects/Types who have a [] operator. Pay attention not to set size higher * than the actual size of the ArrayType as this will lead to unexpected results. */ template inline bool EqualArray( TArrayType1 &arrayType1, TArrayType2 &arrayType2, int size, ScalarType eps = mitk::eps, bool verbose = false) { bool isEqual = true; for (int var = 0; var < size; ++var) { isEqual = isEqual && Equal(arrayType1[var], arrayType2[var], eps); } ConditionalOutputOfDifference(arrayType1, arrayType2, eps, verbose, isEqual); return isEqual; } } #endif /* MITKARRAY_H_ */ diff --git a/Modules/Core/include/mitkBaseData.h b/Modules/Core/include/mitkBaseData.h index c43122796f..14ab1c1856 100644 --- a/Modules/Core/include/mitkBaseData.h +++ b/Modules/Core/include/mitkBaseData.h @@ -1,388 +1,388 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #define BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #include #include "mitkBaseProcess.h" #include "mitkIdentifiable.h" #include "mitkIPropertyOwner.h" #include "mitkOperationActor.h" #include "mitkPropertyList.h" #include "mitkTimeGeometry.h" #include namespace mitk { // class BaseProcess; //##Documentation //## @brief Base of all data objects //## //## Base of all data objects, e.g., images, contours, surfaces etc. Inherits //## from itk::DataObject and thus can be included in a pipeline. //## Inherits also from OperationActor and can be used as a destination for Undo //## @remark Some derived classes may support the persistence of the Identifiable UID. //** but it is no guaranteed feature and also depends on the format the data is stored in //** as not all formats support storing of meta information. Please check the documentation //** of the IFileReader and IFileWriter classes to see if the ID-persistance is supported. //** MITK SceneIO supports the UID persistance for all BaseData derived classes. //## @ingroup Data class MITKCORE_EXPORT BaseData : public itk::DataObject, public OperationActor, public Identifiable, public IPropertyOwner { public: mitkClassMacroItkParent(BaseData, itk::DataObject); // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; /** * \brief Return the TimeGeometry of the data as const pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ const mitk::TimeGeometry *GetTimeGeometry() const { return m_TimeGeometry.GetPointer(); } /** * @brief Return the TimeGeometry of the data as pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ mitk::TimeGeometry *GetTimeGeometry() { return m_TimeGeometry.GetPointer(); } /** * @brief Return the TimeGeometry of the data. * * The method does not simply return the value of the m_TimeGeometry * member. Before doing this, it makes sure that the TimeGeometry * is up-to-date (by setting the update extent to largest possible and * calling UpdateOutputInformation). */ const mitk::TimeGeometry *GetUpdatedTimeGeometry(); /** * \brief Expands the TimeGeometry to a number of TimeSteps. * * The method expands the TimeGeometry to the given number of TimeSteps, * filling newly created elements with empty geometries. Sub-classes should override * this method to handle the elongation of their data vectors, too. * Note that a shrinking is neither possible nor intended. */ virtual void Expand(unsigned int timeSteps); /** * \brief Return the BaseGeometry of the data at time \a t. * * The method does not simply return * m_TimeGeometry->GetGeometry(t). * Before doing this, it makes sure that the BaseGeometry is up-to-date * (by setting the update extent appropriately and calling * UpdateOutputInformation). * * @todo Appropriate setting of the update extent is missing. */ const mitk::BaseGeometry *GetUpdatedGeometry(int t = 0); //##Documentation //## @brief Return the geometry, which is a TimeGeometry, of the data //## as non-const pointer. //## //## \warning No update will be called. Use GetUpdatedGeometry() if you cannot //## be sure that the geometry is up-to-date. //## //## Normally used in GenerateOutputInformation of subclasses of BaseProcess. mitk::BaseGeometry *GetGeometry(int t = 0) const { if (m_TimeGeometry.IsNull()) return nullptr; return m_TimeGeometry->GetGeometryForTimeStep(t); } //##Documentation //## @brief Update the information for this BaseData (the geometry in particular) //## so that it can be used as an output of a BaseProcess. //## //## This method is used in the pipeline mechanism to propagate information and //## initialize the meta data associated with a BaseData. Any implementation //## of this method in a derived class is assumed to call its source's //## BaseProcess::UpdateOutputInformation() which determines modified //## times, LargestPossibleRegions, and any extra meta data like spacing, //## origin, etc. Default implementation simply call's it's source's //## UpdateOutputInformation(). //## \note Implementations of this methods in derived classes must take care //## that the geometry is updated by calling //## GetTimeGeometry()->UpdateInformation() //## \em after calling its source's BaseProcess::UpdateOutputInformation(). void UpdateOutputInformation() override; //##Documentation //## @brief Set the RequestedRegion to the LargestPossibleRegion. //## //## This forces a filter to produce all of the output in one execution //## (i.e. not streaming) on the next call to Update(). void SetRequestedRegionToLargestPossibleRegion() override = 0; //##Documentation //## @brief Determine whether the RequestedRegion is outside of the BufferedRegion. //## //## This method returns true if the RequestedRegion //## is outside the BufferedRegion (true if at least one pixel is //## outside). This is used by the pipeline mechanism to determine //## whether a filter needs to re-execute in order to satisfy the //## current request. If the current RequestedRegion is already //## inside the BufferedRegion from the previous execution (and the //## current filter is up to date), then a given filter does not need //## to re-execute bool RequestedRegionIsOutsideOfTheBufferedRegion() override = 0; //##Documentation //## @brief Verify that the RequestedRegion is within the LargestPossibleRegion. //## //## If the RequestedRegion is not within the LargestPossibleRegion, //## then the filter cannot possibly satisfy the request. This method //## returns true if the request can be satisfied (even if it will be //## necessary to process the entire LargestPossibleRegion) and //## returns false otherwise. This method is used by //## PropagateRequestedRegion(). PropagateRequestedRegion() throws a //## InvalidRequestedRegionError exception if the requested region is //## not within the LargestPossibleRegion. bool VerifyRequestedRegion() override = 0; //##Documentation //## @brief Copy information from the specified data set. //## //## This method is part of the pipeline execution model. By default, a //## BaseProcess will copy meta-data from the first input to all of its //## outputs. See ProcessObject::GenerateOutputInformation(). Each //## subclass of DataObject is responsible for being able to copy //## whatever meta-data it needs from another DataObject. //## The default implementation of this method copies the time sliced geometry //## and the property list of an object. If a subclass overrides this //## method, it should always call its superclass' version. void CopyInformation(const itk::DataObject *data) override; //##Documentation //## @brief Check whether the data has been initialized, i.e., //## at least the Geometry and other header data has been set //## //## \warning Set to \a true by default for compatibility reasons. //## Set m_Initialized=false in constructors of sub-classes that //## support distinction between initialized and uninitialized state. virtual bool IsInitialized() const; //##Documentation //## @brief Calls ClearData() and InitializeEmpty(); //## \warning Only use in subclasses that reimplemented these methods. //## Just calling Clear from BaseData will reset an object to a not initialized, //## invalid state. virtual void Clear(); //##Documentation //## @brief Check whether object contains data (at //## a specified time), e.g., a set of points may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmptyTimeStep(unsigned int t) const; //##Documentation //## @brief Check whether object contains data (at //## least at one point in time), e.g., a set of points //## may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmpty() const; //##Documentation //## @brief Set the requested region from this data object to match the requested //## region of the data object passed in as a parameter. //## //## This method is implemented in the concrete subclasses of BaseData. void SetRequestedRegion(const itk::DataObject *data) override = 0; //##Documentation //##@brief overwrite if the Data can be called by an Interactor (StateMachine). //## //## Empty by default. Overwrite and implement all the necessary operations here //## and get the necessary information from the parameter operation. void ExecuteOperation(Operation *operation) override; /** * \brief Set the BaseGeometry of the data, which will be referenced (not copied!). * Assumes the data object has only 1 time step ( is a 3D object ) and creates a * new TimeGeometry which saves the given BaseGeometry. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedGeometry */ virtual void SetGeometry(BaseGeometry *aGeometry3D); /** * \brief Set the TimeGeometry of the data, which will be referenced (not copied!). * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedTimeGeometry */ virtual void SetTimeGeometry(TimeGeometry *geometry); /** * \brief Set a clone of the provided Geometry as Geometry of the data. * Assumes the data object has only 1 time step ( is a 3D object ) and * creates a new TimeGeometry. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * \sa SetGeometry */ virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D); /** * \brief Set a clone of the provided TimeGeometry as TimeGeometry of the data. * * \sa SetGeometry */ virtual void SetClonedTimeGeometry(const TimeGeometry *geometry); //##Documentation //## @brief Set a clone of the provided geometry as BaseGeometry of a given time step. //## //## \sa SetGeometry virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D, unsigned int time); //##Documentation //## @brief Get the data's property list //## @sa GetProperty //## @sa m_PropertyList mitk::PropertyList::Pointer GetPropertyList() const; //##Documentation //## @brief Set the data's property list //## @sa SetProperty //## @sa m_PropertyList void SetPropertyList(PropertyList *propertyList); //##Documentation //## @brief Get the property (instance of BaseProperty) with key @a propertyKey from the PropertyList, //## and set it to this, respectively; //## @sa GetPropertyList //## @sa m_PropertyList //## @sa m_MapOfPropertyLists mitk::BaseProperty::Pointer GetProperty(const char *propertyKey) const; void SetProperty(const char *propertyKey, BaseProperty *property); //##Documentation //## @brief Convenience method for setting the origin of //## the BaseGeometry instances of all time steps //## //## \warning Geometries contained in the BaseGeometry will //## \em not be changed, e.g. in case the BaseGeometry is a //## SlicedGeometry3D the origin will \em not be propagated //## to the contained slices. The sub-class SlicedData //## does this for the case that the SlicedGeometry3D is //## evenly spaced. virtual void SetOrigin(const Point3D &origin); /** \brief Get the process object that generated this data object. * * If there is no process object, then the data object has * been disconnected from the pipeline, or the data object * was created manually. (Note: we cannot use the GetObjectMacro() * defined in itkMacro because the mutual dependency of * DataObject and ProcessObject causes compile problems. Also, * a forward reference smart pointer is returned, not a smart pointer, * because of the circular dependency between the process and data object.) * * GetSource() returns a SmartPointer and not a WeakPointer * because it is assumed the code calling GetSource() wants to hold a * long term reference to the source. */ itk::SmartPointer GetSource() const; //##Documentation //## @brief Get the number of time steps from the TimeGeometry //## As the base data has not a data vector given by itself, the number //## of time steps is defined over the time sliced geometry. In sub classes, //## a better implementation could be over the length of the data vector. unsigned int GetTimeSteps() const { return m_TimeGeometry->CountTimeSteps(); } //##Documentation //## @brief Get the modified time of the last change of the contents //## this data object or its geometry. - unsigned long GetMTime() const override; + itk::ModifiedTimeType GetMTime() const override; /** * \sa itk::ProcessObject::Graft */ void Graft(const DataObject *) override; protected: BaseData(); BaseData(const BaseData &other); ~BaseData() override; //##Documentation //## \brief Initialize the TimeGeometry for a number of time steps. //## The TimeGeometry is initialized empty and evenly timed. //## In many cases it will be necessary to overwrite this in sub-classes. virtual void InitializeTimeGeometry(unsigned int timeSteps = 1); //##Documentation //## @brief reset to non-initialized state, release memory virtual void ClearData(); //##Documentation //## @brief Pure virtual; Must be used in subclasses to get a data object to a //## valid state. Should at least create one empty object and call //## Superclass::InitializeTimeGeometry() to ensure an existing valid geometry virtual void InitializeEmpty() {} void PrintSelf(std::ostream &os, itk::Indent indent) const override; bool m_LastRequestedRegionWasOutsideOfTheBufferedRegion; mutable unsigned int m_SourceOutputIndexDuplicate; bool m_Initialized; private: //##Documentation //## @brief PropertyList, f.e. to hold pic-tags, tracking-data,.. //## PropertyList::Pointer m_PropertyList; TimeGeometry::Pointer m_TimeGeometry; }; } // namespace mitk #endif /* BASEDATA_H_HEADER_INCLUDED_C1EBB6FA */ diff --git a/Modules/Core/include/mitkBaseGeometry.h b/Modules/Core/include/mitkBaseGeometry.h index bda3ebf7fc..040e5e8134 100644 --- a/Modules/Core/include/mitkBaseGeometry.h +++ b/Modules/Core/include/mitkBaseGeometry.h @@ -1,755 +1,752 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef BaseGeometry_H_HEADER_INCLUDED #define BaseGeometry_H_HEADER_INCLUDED #include "mitkOperationActor.h" #include #include #include "itkScalableAffineTransform.h" #include "mitkNumericTypes.h" -#include #include #include #include #include #include #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; namespace mitk { //##Documentation //## @brief Standard 3D-BoundingBox typedef //## //## Standard 3D-BoundingBox typedef to get rid of template arguments (3D, type). typedef itk::BoundingBox BoundingBox; //##Documentation //## @brief Standard typedef for time-bounds typedef itk::FixedArray TimeBounds; typedef itk::FixedArray FixedArrayType; - typedef itk::AffineGeometryFrame AffineGeometryFrame3D; - //##Documentation //## @brief BaseGeometry Describes the geometry of a data object //## //## The class holds //## \li a bounding box which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels), to be accessed by //## GetBoundingBox() //## \li a transform to convert intrinsic coordinates into a //## world-coordinate system with coordinates in millimeters //## and milliseconds (all are floating point values), to //## be accessed by GetIndexToWorldTransform() //## \li an origin and spacing to define the geometry //## //## BaseGeometry and its sub-classes allow converting between //## intrinsic coordinates (called index or unit coordinates) //## and world-coordinates (called world or mm coordinates), //## e.g. WorldToIndex. //## In case you need integer index coordinates, provide an //## mitk::Index3D (or itk::Index) as target variable to //## WorldToIndex, otherwise you will get a continuous index //## (floating point values). //## //## An important sub-class is SlicedGeometry3D, which descibes //## data objects consisting of slices, e.g., objects of type Image. //## Conversions between world coordinates (in mm) and unit coordinates //## (e.g., pixels in the case of an Image) can be performed. //## //## For more information on related classes, see \ref Geometry. //## //## BaseGeometry instances referring to an Image need a slightly //## different definition of corners, see SetImageGeometry. This //## is usualy automatically called by Image. //## //## BaseGeometry have to be initialized in the method GenerateOutputInformation() //## of BaseProcess (or CopyInformation/ UpdateOutputInformation of BaseData, //## if possible, e.g., by analyzing pic tags in Image) subclasses. See also //## itk::ProcessObject::GenerateOutputInformation(), //## itk::DataObject::CopyInformation() and //## itk::DataObject::UpdateOutputInformation(). //## //## At least, it can return the bounding box of the data object. //## //## The BaseGeometry class is an abstract class. The most simple implementation //## is the sublass Geometry3D. //## //## Rule: everything is in mm (ms) if not stated otherwise. //## @ingroup Geometry class MITKCORE_EXPORT BaseGeometry : public itk::Object, public OperationActor { public: mitkClassMacroItkParent(BaseGeometry, itk::Object); itkCloneMacro(Self); // ********************************** TypeDef ********************************** typedef GeometryTransformHolder::TransformType TransformType; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // ********************************** Origin, Spacing ********************************** //##Documentation //## @brief Get the origin, e.g. the upper-left corner of the plane const Point3D GetOrigin() const; //##Documentation //## @brief Set the origin, i.e. the upper-left corner of the plane //## void SetOrigin(const Point3D &origin); //##Documentation //## @brief Get the spacing (size of a pixel). //## const mitk::Vector3D GetSpacing() const; //##Documentation //## @brief Set the spacing (m_Spacing). //## //##The spacing is also changed in the IndexToWorldTransform. void SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief Get the origin as VnlVector //## //## \sa GetOrigin VnlVector GetOriginVnl() const; // ********************************** other functions ********************************** //##Documentation //## @brief Get the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkGetConstMacro(FrameOfReferenceID, unsigned int); //##Documentation //## @brief Set the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkSetMacro(FrameOfReferenceID, unsigned int); itkGetConstMacro(IndexToWorldTransformLastModified, unsigned long); //##Documentation //## @brief Overload of function Modified() to prohibit several calls of Modified() using the ModifiedLock class. //## //## For the use of Modified(), see class ModifiedLock. void Modified() const override; friend class ModifiedLock; //##Documentation //## @brief Is this BaseGeometry in a state that is valid? //## //## This function returns always true in the BaseGeometry class. Other implementations are possible in subclasses. virtual bool IsValid() const; // ********************************** Initialize ********************************** //##Documentation //## @brief Initialize the BaseGeometry void Initialize(); void InitializeGeometry(Self *newGeometry) const; // ********************************** Transformations Set/Get ********************************** //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates mitk::AffineTransform3D *GetIndexToWorldTransform(); //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates const mitk::AffineTransform3D *GetIndexToWorldTransform() const; //## @brief Set the transformation used to convert from index //## to world coordinates. The spacing of the new transform is //## copied to m_spacing. void SetIndexToWorldTransform(mitk::AffineTransform3D *transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4.The spacing of //## the new transform is copied to m_spacing. //## \sa SetIndexToWorldTransform void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix); //## @brief Set the transformation used to convert from index //## to world coordinates.This function keeps the original spacing. void SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4. This function keeps the original spacing. //## \sa SetIndexToWorldTransform void SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix); //## Get the Vtk Matrix which describes the transform. vtkMatrix4x4 *GetVtkMatrix(); //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform vtkLinearTransform *GetVtkTransform() const; //##Documentation //## @brief Set the transform to identity, the spacing to 1 and origin to 0 //## void SetIdentity(); // ********************************** Transformations ********************************** //##Documentation //## @brief Compose new IndexToWorldTransform with a given transform. //## //## This method composes m_IndexToWorldTransform with another transform, //## modifying self to be the composition of self and other. //## If the argument pre is true, then other is precomposed with self; //## that is, the resulting transformation consists of first applying //## other to the source, followed by self. If pre is false or omitted, //## then other is post-composed with self; that is the resulting //## transformation consists of first applying self to the source, //## followed by other. //## This method also changes m_spacing. void Compose(const TransformType *other, bool pre = false); //##Documentation //## @brief Compose new IndexToWorldTransform with a given vtkMatrix4x4. //## //## Converts the vtkMatrix4x4 into a itk-transform and calls the previous method. void Compose(const vtkMatrix4x4 *vtkmatrix, bool pre = false); //##Documentation //## @brief Translate the origin by a vector //## void Translate(const Vector3D &vector); //##Documentation //##@brief executes affine operations (translate, rotate, scale) void ExecuteOperation(Operation *operation) override; //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (continuous!) index coordinates //## \warning If you need (discrete) integer index coordinates (e.g., for iterating easily over an image), //## use WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index). //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (discrete!) index coordinates. //## This method rounds to integer indices! //## For further information about coordinates types, please see the Geometry documentation template void WorldToIndex(const mitk::Point3D &pt_mm, itk::Index &index) const { typedef itk::Index IndexType; mitk::Point3D pt_units; this->WorldToIndex(pt_mm, pt_units); int i, dim = index.GetIndexDimension(); if (dim > 3) { index.Fill(0); dim = 3; } for (i = 0; i < dim; ++i) { index[i] = itk::Math::RoundHalfIntegerUp(pt_units[i]); } } //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const; //##Documentation //## @brief Convert (discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation template void IndexToWorld(const itk::Index &index, mitk::Point3D &pt_mm) const { mitk::Point3D pt_units; pt_units.Fill(0); int i, dim = index.GetIndexDimension(); if (dim > 3) { dim = 3; } for (i = 0; i < dim; ++i) { pt_units[i] = index[i]; } IndexToWorld(pt_units, pt_mm); } //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## @deprecated First parameter (Point3D) is not used. If possible, please use void IndexToWorld(const // mitk::Vector3D& vec_units, mitk::Vector3D& vec_mm) const. //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D &atPt3d_units, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## @deprecated First parameter (Point3D) is not used. If possible, please use void WorldToIndex(const // mitk::Vector3D& vec_mm, mitk::Vector3D& vec_units) const. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const; //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert ITK physical coordinates of a \em point (in mm, //## but without a rotation) into MITK world coordinates (in mm) //## //## For more information, see WorldToItkPhysicalPoint. template void ItkPhysicalPointToWorld(const itk::Point &itkPhysicalPoint, mitk::Point3D &pt_mm) const { mitk::vtk2itk(itkPhysicalPoint, pt_mm); } //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert world coordinates (in mm) of a \em point to //## ITK physical coordinates (in mm, but without a possible rotation) //## //## This method is useful if you have want to access an mitk::Image //## via an itk::Image. ITK v3.8 and older did not support rotated (tilted) //## images, i.e., ITK images are always parallel to the coordinate axes. //## When accessing a (possibly rotated) mitk::Image via an itk::Image //## the rotational part of the transformation in the BaseGeometry is //## simply discarded; in other word: only the origin and spacing is //## used by ITK, not the complete matrix available in MITK. //## With WorldToItkPhysicalPoint you can convert an MITK world //## coordinate (including the rotation) into a coordinate that //## can be used with the ITK image as a ITK physical coordinate //## (excluding the rotation). template void WorldToItkPhysicalPoint(const mitk::Point3D &pt_mm, itk::Point &itkPhysicalPoint) const { mitk::vtk2itk(pt_mm, itkPhysicalPoint); } // ********************************** BoundingBox ********************************** /** Get the bounding box */ itkGetConstObjectMacro(BoundingBox, BoundingBoxType); // a bit of a misuse, but we want only doxygen to see the following: #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get bounding box (in index/unit coordinates) itkGetConstObjectMacro(BoundingBox, BoundingBoxType); //##Documentation //## @brief Get bounding box (in index/unit coordinates) as a BoundsArrayType const BoundsArrayType GetBounds() const; #endif const BoundsArrayType GetBounds() const; //##Documentation //## \brief Set the bounding box (in index/unit coordinates) //## //## Only possible via the BoundsArray to make clear that a //## copy of the bounding-box is stored, not a reference to it. void SetBounds(const BoundsArrayType &bounds); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a float array void SetFloatBounds(const float bounds[6]); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a double array void SetFloatBounds(const double bounds[6]); //##Documentation //## @brief Get a VnlVector along bounding-box in the specified //## @a direction, length is spacing //## //## \sa GetAxisVector VnlVector GetMatrixColumn(unsigned int direction) const; //##Documentation //## @brief Calculates a bounding-box around the geometry relative //## to a coordinate system defined by a transform //## mitk::BoundingBox::Pointer CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D *transform) const; //##Documentation //## @brief Set the time bounds (in ms) // void SetTimeBounds(const TimeBounds& timebounds); // ********************************** Geometry ********************************** #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get the extent of the bounding box (in index/unit coordinates) //## //## To access the extent in mm use GetExtentInMM ScalarType GetExtent(unsigned int direction) const; #endif /** Get the extent of the bounding box */ ScalarType GetExtent(unsigned int direction) const; //##Documentation //## @brief Get the extent of the bounding-box in the specified @a direction in mm //## //## Equals length of GetAxisVector(direction). ScalarType GetExtentInMM(int direction) const; //##Documentation //## @brief Get vector along bounding-box in the specified @a direction in mm //## //## The length of the vector is the size of the bounding-box in the //## specified @a direction in mm //## \sa GetMatrixColumn Vector3D GetAxisVector(unsigned int direction) const; //##Documentation //## @brief Checks, if the given geometry can be converted to 2D without information loss //## e.g. when a 2D image is saved, the matrix is usually cropped to 2x2, and when you load it back to MITK //## it will be filled with standard values. This function checks, if information would be lost during this //## procedure virtual bool Is2DConvertable(); //##Documentation //## @brief Get the center of the bounding-box in mm //## Point3D GetCenter() const; //##Documentation //## @brief Get the squared length of the diagonal of the bounding-box in mm //## double GetDiagonalLength2() const; //##Documentation //## @brief Get the length of the diagonal of the bounding-box in mm //## double GetDiagonalLength() const; //##Documentation //## @brief Get the position of the corner number \a id (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(int id) const; //##Documentation //## @brief Get the position of a corner (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(bool xFront = true, bool yFront = true, bool zFront = true) const; //##Documentation //## @brief Set the extent of the bounding-box in the specified @a direction in mm //## //## @note This changes the matrix in the transform, @a not the bounds, which are given in units! void SetExtentInMM(int direction, ScalarType extentInMM); //##Documentation //## @brief Test whether the point \a p (world coordinates in mm) is //## inside the bounding box bool IsInside(const mitk::Point3D &p) const; //##Documentation //## @brief Test whether the point \a p ((continous!)index coordinates in units) is //## inside the bounding box bool IsIndexInside(const mitk::Point3D &index) const; //##Documentation //## @brief Convenience method for working with ITK indices template bool IsIndexInside(const itk::Index &index) const { int i, dim = index.GetIndexDimension(); Point3D pt_index; pt_index.Fill(0); for (i = 0; i < dim; ++i) { pt_index[i] = index[i]; } return IsIndexInside(pt_index); } // ********************************* Image Geometry ******************************** //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to //change // the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and // changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry); //##Documentation //## @brief Is this an ImageGeometry? //## //## For more information, see SetImageGeometry itkGetConstMacro(ImageGeometry, bool) //##Documentation //## @brief Define that this BaseGeometry is refering to an Image //## //## A geometry referring to an Image needs a slightly different //## definition of the position of the corners (see GetCornerPoint). //## The position of a voxel is defined by the position of its center. //## If we would use the origin (position of the (center of) the first //## voxel) as a corner and display this point, it would seem to be //## \em not at the corner but a bit within the image. Even worse for //## the opposite corner of the image: here the corner would appear //## outside the image (by half of the voxel diameter). Thus, we have //## to correct for this and to be able to do that, we need to know //## that the BaseGeometry is referring to an Image. itkSetMacro(ImageGeometry, bool); itkBooleanMacro(ImageGeometry); const GeometryTransformHolder *GetGeometryTransformHolder() const; protected: // ********************************** Constructor ********************************** BaseGeometry(); BaseGeometry(const BaseGeometry &other); ~BaseGeometry() override; itk::LightObject::Pointer InternalClone() const override = 0; void PrintSelf(std::ostream &os, itk::Indent indent) const override; static const std::string GetTransformAsString(TransformType *transformType); itkGetConstMacro(NDimensions, unsigned int); bool IsBoundingBoxNull() const; bool IsIndexToWorldTransformNull() const; void SetVtkMatrixDeepCopy(vtkTransform *vtktransform); void _SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief PreSetSpacing //## //## These virtual function allows a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use //## {Superclass::PreSetSpacing();}; virtual void PreSetSpacing(const mitk::Vector3D & /*aSpacing*/){}; //##Documentation //## @brief CheckBounds //## //## This function is called in SetBounds. Assertions can be implemented in this function (see PlaneGeometry.cpp). //## If you implement this function in a subclass, make sure, that all classes were your class inherits from //## have an implementation of CheckBounds //## (e.g. inheritance BaseGeometry <- A <- B. Implementation of CheckBounds in class B needs implementation in A as // well!) virtual void CheckBounds(const BoundsArrayType & /*bounds*/){}; //##Documentation //## @brief CheckIndexToWorldTransform //## //## This function is called in SetIndexToWorldTransform. Assertions can be implemented in this function (see // PlaneGeometry.cpp). //## In Subclasses of BaseGeometry, implement own conditions or call Superclass::CheckBounds(bounds);. virtual void CheckIndexToWorldTransform(mitk::AffineTransform3D * /*transform*/){}; private: GeometryTransformHolder *m_GeometryTransform; void InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry); //##Documentation //## @brief Bounding Box, which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels) BoundingBoxPointer m_BoundingBox; unsigned int m_FrameOfReferenceID; // mitk::TimeBounds m_TimeBounds; static const unsigned int m_NDimensions = 3; mutable TransformType::Pointer m_InvertedTransform; mutable unsigned long m_IndexToWorldTransformLastModified; bool m_ImageGeometry; //##Documentation //## @brief ModifiedLockFlag is used to prohibit the call of Modified() //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the ModifiedLock class! bool m_ModifiedLockFlag; //##Documentation //## @brief ModifiedcalledFlag is used to collect calls of Modified(). //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the Modified() function! mutable bool m_ModifiedCalledFlag; }; // ********************************** Equal Functions ********************************** // // Static compare functions mainly for testing // /** * @brief Equal A function comparing two geometries for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the spacing, origin, axisvectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * If you want to use different tolerance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param coordinateEps Tolerance for comparison of all spatial aspects (spacing, origin and grid alignment). * You can use mitk::eps in most cases. * @param directionEps Tolerance for comparison of all directional aspects (axis). You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType coordinateEps, ScalarType directionEps, bool verbose = false); /** * @brief Equal A function comparing two geometries for beeing identical. * * @ingroup MITKTestingAPI * * This is an overloaded version that uses a single tolerance for spatial and directional aspects. For more details, * see the other overloaded version. * * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, ScalarType eps = mitk::eps, bool verbose = false); /** * @brief Equal A function comparing two transforms (TransformType) for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the IndexToWorldTransform (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose); /** * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the bounds (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose); /** * @brief A function checks if a test geometry is a sub geometry of * a given reference geometry. * * Sub geometry means that both geometries have the same voxel grid (same spacing, same axes, * orgin is on voxel grid), but the bounding box of the checked geometry is contained or equal * to the bounding box of the reference geometry.\n * By this definition equal geometries are always sub geometries of each other. * * The function checks the spacing, origin, axis vectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * * The parameter eps is a tolerance value for all methods which are internally used for comparison. * @param testGeo Geometry that should be checked if it is a sub geometry of referenceGeo. * @param referenceGeo Geometry that should contain testedGeometry as sub geometry. * @param coordinateEps Tolerance for comparison of all spatial aspects (spacing, origin and grid alignment). * You can use mitk::eps in most cases. * @param directionEps Tolerance for comparison of all directional aspects (axis). You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparisons are true. False otherwise. */ MITKCORE_EXPORT bool IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, ScalarType coordinateEps, ScalarType directionEps, bool verbose = false); /** * @brief A function checks if a test geometry is a sub geometry of * a given reference geometry. * * This is a overloaded version that uses a single tolerance for spatial and directional aspects. For more details, * see the other overloaded version. * * @param testGeo Geometry that should be checked if it is a sub geometry of referenceGeo. * @param referenceGeo Geometry that should contain testedGeometry as sub geometry. * @param eps Tolarence for comparison (both spatial and directional). You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False otherwise. */ MITKCORE_EXPORT bool IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, ScalarType eps = mitk::eps, bool verbose = false); } // namespace mitk #endif /* BaseGeometry_H_HEADER_INCLUDED */ diff --git a/Modules/Core/include/mitkBaseRenderer.h b/Modules/Core/include/mitkBaseRenderer.h index 909de3a27c..8d2a45ef67 100644 --- a/Modules/Core/include/mitkBaseRenderer.h +++ b/Modules/Core/include/mitkBaseRenderer.h @@ -1,523 +1,523 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 #define BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 #include "mitkCameraRotationController.h" #include "mitkDataStorage.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneGeometryData.h" #include "mitkSliceNavigationController.h" #include "mitkTimeGeometry.h" #include "mitkBindDispatcherInteractor.h" #include "mitkDispatcher.h" #include #include #include #include namespace mitk { class NavigationController; class SliceNavigationController; class CameraRotationController; class CameraController; class DataStorage; class Mapper; class BaseLocalStorageHandler; class KeyEvent; +#pragma GCC visibility push(default) + itkEventMacroDeclaration(RendererResetEvent, itk::AnyEvent); +#pragma GCC visibility pop + //##Documentation //## @brief Organizes the rendering process //## //## Organizes the rendering process. A Renderer contains a reference to a //## DataStorage and asks the mappers of the data objects to render //## the data into the renderwindow it is associated to. //## //## \#Render() checks if rendering is currently allowed by calling //## RenderWindow::PrepareRendering(). Initialization of a rendering context //## can also be performed in this method. //## //## The actual rendering code has been moved to \#Repaint() //## Both \#Repaint() and \#Update() are declared protected now. //## //## Note: Separation of the Repaint and Update processes (rendering vs //## creating a vtk prop tree) still needs to be worked on. The whole //## rendering process also should be reworked to use VTK based classes for //## both 2D and 3D rendering. //## @ingroup Renderer class MITKCORE_EXPORT BaseRenderer : public itk::Object { public: typedef std::map BaseRendererMapType; static BaseRendererMapType baseRendererMap; static BaseRenderer *GetInstance(vtkRenderWindow *renWin); static void AddInstance(vtkRenderWindow *renWin, BaseRenderer *baseRenderer); static void RemoveInstance(vtkRenderWindow *renWin); static BaseRenderer *GetByName(const std::string &name); static vtkRenderWindow *GetRenderWindowByName(const std::string &name); -#pragma GCC visibility push(default) - itkEventMacro(RendererResetEvent, itk::AnyEvent); -#pragma GCC visibility pop - /** Standard class typedefs. */ mitkClassMacroItkParent(BaseRenderer, itk::Object); BaseRenderer(const char *name = nullptr, vtkRenderWindow *renWin = nullptr); //##Documentation //## @brief MapperSlotId defines which kind of mapper (e.g. 2D or 3D) should be used. typedef int MapperSlotId; enum StandardMapperSlot { Standard2D = 1, Standard3D = 2 }; //##Documentation //## @brief Possible view directions for render windows. enum class ViewDirection { AXIAL = 0, SAGITTAL, CORONAL, THREE_D }; virtual void SetDataStorage(DataStorage *storage); ///< set the datastorage that will be used for rendering //##Documentation //## return the DataStorage that is used for rendering virtual DataStorage::Pointer GetDataStorage() const { return m_DataStorage.GetPointer(); } //##Documentation //## @brief Access the RenderWindow into which this renderer renders. vtkRenderWindow *GetRenderWindow() const { return m_RenderWindow; } vtkRenderer *GetVtkRenderer() const { return m_VtkRenderer; } //##Documentation //## @brief Returns the Dispatcher which handles Events for this BaseRenderer Dispatcher::Pointer GetDispatcher() const; //##Documentation //## @brief Default mapper id to use. static const MapperSlotId defaultMapper; //##Documentation //## @brief Do the rendering and flush the result. virtual void Paint(); //##Documentation //## @brief Initialize the RenderWindow. Should only be called from RenderWindow. virtual void Initialize(); //##Documentation //## @brief Called to inform the renderer that the RenderWindow has been resized. virtual void Resize(int w, int h); //##Documentation //## @brief Initialize the renderer with a RenderWindow (@a renderwindow). virtual void InitRenderer(vtkRenderWindow *renderwindow); //##Documentation //## @brief Set the initial size. Called by RenderWindow after it has become //## visible for the first time. virtual void InitSize(int w, int h); //##Documentation //## @brief Draws a point on the widget. //## Should be used during conferences to show the position of the remote mouse virtual void DrawOverlayMouse(Point2D &p2d); //##Documentation //## @brief Set/Get the WorldGeometry (m_WorldGeometry) for 3D and 2D rendering, that describing the //## (maximal) area to be rendered. //## //## Depending of the type of the passed BaseGeometry more or less information can be extracted: //## \li if it is a PlaneGeometry (which is a sub-class of BaseGeometry), m_CurrentWorldPlaneGeometry is //## also set to point to it. m_WorldTimeGeometry is set to nullptr. //## \li if it is a TimeGeometry, m_WorldTimeGeometry is also set to point to it. //## If m_WorldTimeGeometry contains instances of SlicedGeometry3D, m_CurrentWorldPlaneGeometry is set to //## one of geometries stored in the SlicedGeometry3D according to the value of m_Slice; otherwise //## a PlaneGeometry describing the top of the bounding-box of the BaseGeometry is set as the //## m_CurrentWorldPlaneGeometry. //## \li otherwise a PlaneGeometry describing the top of the bounding-box of the BaseGeometry //## is set as the m_CurrentWorldPlaneGeometry. m_WorldTimeGeometry is set to nullptr. //## @todo add calculation of PlaneGeometry describing the top of the bounding-box of the BaseGeometry //## when the passed BaseGeometry is not sliced. //## \sa m_WorldGeometry //## \sa m_WorldTimeGeometry //## \sa m_CurrentWorldPlaneGeometry virtual void SetWorldGeometry3D(const BaseGeometry *geometry); virtual void SetWorldTimeGeometry(const mitk::TimeGeometry *geometry); itkGetConstObjectMacro(WorldTimeGeometry, TimeGeometry); //##Documentation //## @brief Get the current 3D-worldgeometry (m_CurrentWorldGeometry) used for 3D-rendering itkGetConstObjectMacro(CurrentWorldGeometry, BaseGeometry); //##Documentation //## @brief Get the current 2D-worldgeometry (m_CurrentWorldPlaneGeometry) used for 2D-rendering itkGetConstObjectMacro(CurrentWorldPlaneGeometry, PlaneGeometry) /** * \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometry */ DEPRECATED(const PlaneGeometry *GetCurrentWorldGeometry2D()) { return GetCurrentWorldPlaneGeometry(); }; //##Documentation //## Calculates the bounds of the DataStorage (if it contains any valid data), //## creates a geometry from these bounds and sets it as world geometry of the renderer. //## //## Call this method to re-initialize the renderer to the current DataStorage //## (e.g. after loading an additional dataset), to ensure that the view is //## aligned correctly. //## \warning This is not implemented yet. virtual bool SetWorldGeometryToDataStorageBounds() { return false; } //##Documentation //## @brief Set/Get m_Slice which defines together with m_TimeStep the 2D geometry //## stored in m_WorldTimeGeometry used as m_CurrentWorldPlaneGeometry //## //## \sa m_Slice virtual void SetSlice(unsigned int slice); itkGetConstMacro(Slice, unsigned int); //##Documentation //## @brief Set/Get m_TimeStep which defines together with m_Slice the 2D geometry //## stored in m_WorldTimeGeometry used as m_CurrentWorldPlaneGeometry //## //## \sa m_TimeStep virtual void SetTimeStep(unsigned int timeStep); itkGetConstMacro(TimeStep, unsigned int); //##Documentation //## @brief Get the time-step of a BaseData object which //## exists at the time of the currently displayed content //## //## Returns -1 or mitk::BaseData::m_TimeSteps if there //## is no data at the current time. //## \sa GetTimeStep, m_TimeStep TimeStepType GetTimeStep(const BaseData *data) const; //##Documentation //## @brief Get the time in ms of the currently displayed content //## //## \sa GetTimeStep, m_TimeStep ScalarType GetTime() const; //##Documentation //## @brief SetWorldGeometry is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometrySendEvent virtual void SetGeometry(const itk::EventObject &geometrySliceEvent); //##Documentation //## @brief UpdateWorldGeometry is called to re-read the 2D geometry from the //## slice navigation controller virtual void UpdateGeometry(const itk::EventObject &geometrySliceEvent); //##Documentation //## @brief SetSlice is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometrySliceEvent virtual void SetGeometrySlice(const itk::EventObject &geometrySliceEvent); //##Documentation //## @brief SetTimeStep is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometryTimeEvent virtual void SetGeometryTime(const itk::EventObject &geometryTimeEvent); //##Documentation //## @brief Get a DataNode pointing to a data object containing the current 2D-worldgeometry // m_CurrentWorldPlaneGeometry (for 2D rendering) itkGetObjectMacro(CurrentWorldPlaneGeometryNode, DataNode) /** * \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometryNode */ DEPRECATED(DataNode *GetCurrentWorldGeometry2DNode()) { return GetCurrentWorldPlaneGeometryNode(); }; //##Documentation //## @brief Sets timestamp of CurrentWorldPlaneGeometry and forces so reslicing in that renderwindow void SendUpdateSlice(); //##Documentation //## @brief Get timestamp of last call of SetCurrentWorldPlaneGeometry unsigned long GetCurrentWorldPlaneGeometryUpdateTime() { return m_CurrentWorldPlaneGeometryUpdateTime; } /** * \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometryUpdateTime */ DEPRECATED(unsigned long GetCurrentWorldGeometry2DUpdateTime()) { return GetCurrentWorldPlaneGeometryUpdateTime(); }; //##Documentation //## @brief Get timestamp of last change of current TimeStep unsigned long GetTimeStepUpdateTime() { return m_TimeStepUpdateTime; } //##Documentation //## @brief Perform a picking: find the x,y,z world coordinate of a //## display x,y coordinate. //## @warning Has to be overwritten in subclasses for the 3D-case. //## //## Implemented here only for 2D-rendering virtual void PickWorldPoint(const Point2D &diplayPosition, Point3D &worldPosition) const = 0; /** \brief Determines the object (mitk::DataNode) closest to the current * position by means of picking * * \warning Implementation currently empty for 2D rendering; intended to be * implemented for 3D renderers */ virtual DataNode *PickObject(const Point2D & /*displayPosition*/, Point3D & /*worldPosition*/) const { return nullptr; } //##Documentation //## @brief Get the MapperSlotId to use. itkGetMacro(MapperID, MapperSlotId); itkGetConstMacro(MapperID, MapperSlotId); //##Documentation //## @brief Set the MapperSlotId to use. virtual void SetMapperID(MapperSlotId id); virtual int *GetSize() const; virtual int *GetViewportSize() const; void SetSliceNavigationController(SliceNavigationController *SlicenavigationController); itkGetObjectMacro(CameraController, CameraController); itkGetObjectMacro(SliceNavigationController, SliceNavigationController); itkGetObjectMacro(CameraRotationController, CameraRotationController); itkGetMacro(EmptyWorldGeometry, bool); //##Documentation //## @brief Tells if the displayed region is shifted and rescaled if the render window is resized. itkGetMacro(KeepDisplayedRegion, bool) //##Documentation //## @brief Tells if the displayed region should be shifted and rescaled if the render window is resized. itkSetMacro(KeepDisplayedRegion, bool); //##Documentation //## @brief get the name of the Renderer //## @note const char *GetName() const { return m_Name.c_str(); } //##Documentation //## @brief get the x_size of the RendererWindow //## @note int GetSizeX() const { return GetSize()[0]; } //##Documentation //## @brief get the y_size of the RendererWindow //## @note int GetSizeY() const { return GetSize()[1]; } const double *GetBounds() const; void RequestUpdate(); void ForceImmediateUpdate(); /** Returns number of mappers which are visible and have level-of-detail * rendering enabled */ unsigned int GetNumberOfVisibleLODEnabledMappers() const; //##Documentation //## @brief This method converts a display point to the 3D world index //## using the geometry of the renderWindow. void DisplayToWorld(const Point2D &displayPoint, Point3D &worldIndex) const; //##Documentation //## @brief This method converts a display point to the 2D world index, mapped onto the display plane //## using the geometry of the renderWindow. void DisplayToPlane(const Point2D &displayPoint, Point2D &planePointInMM) const; //##Documentation //## @brief This method converts a 3D world index to the display point //## using the geometry of the renderWindow. void WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const; //##Documentation //## @brief This method converts a 3D world index to the point on the viewport //## using the geometry of the renderWindow. void WorldToView(const Point3D &worldIndex, Point2D &viewPoint) const; //##Documentation //## @brief This method converts a 2D plane coordinate to the display point //## using the geometry of the renderWindow. void PlaneToDisplay(const Point2D &planePointInMM, Point2D &displayPoint) const; //##Documentation //## @brief This method converts a 2D plane coordinate to the point on the viewport //## using the geometry of the renderWindow. void PlaneToView(const Point2D &planePointInMM, Point2D &viewPoint) const; double GetScaleFactorMMPerDisplayUnit() const; Point2D GetDisplaySizeInMM() const; Point2D GetViewportSizeInMM() const; Point2D GetOriginInMM() const; itkGetConstMacro(ConstrainZoomingAndPanning, bool) virtual void SetConstrainZoomingAndPanning(bool constrain); /** * \brief Provides (1) world coordinates for a given mouse position and (2) * translates mousePosition to Display coordinates * \deprecated Map2DRendererPositionTo3DWorldPosition is deprecated. Please use DisplayToWorld instead. */ DEPRECATED(virtual Point3D Map2DRendererPositionTo3DWorldPosition(const Point2D &mousePosition) const); protected: ~BaseRenderer() override; //##Documentation //## @brief Call update of all mappers. To be implemented in subclasses. virtual void Update() = 0; vtkRenderWindow *m_RenderWindow; vtkRenderer *m_VtkRenderer; //##Documentation //## @brief MapperSlotId to use. Defines which kind of mapper (e.g., 2D or 3D) shoud be used. MapperSlotId m_MapperID; //##Documentation //## @brief The DataStorage that is used for rendering. DataStorage::Pointer m_DataStorage; //##Documentation //## @brief Timestamp of last call of Update(). unsigned long m_LastUpdateTime; //##Documentation //## @brief CameraController for 3D rendering //## @note preliminary. itk::SmartPointer m_CameraController; SliceNavigationController::Pointer m_SliceNavigationController; CameraRotationController::Pointer m_CameraRotationController; //##Documentation //## @brief Sets m_CurrentWorldPlaneGeometry virtual void SetCurrentWorldPlaneGeometry(const PlaneGeometry *geometry2d); /** * \deprecatedSince{2014_10} Please use SetCurrentWorldPlaneGeometry */ DEPRECATED(void SetCurrentWorldGeometry2D(PlaneGeometry *geometry2d)) { SetCurrentWorldPlaneGeometry(geometry2d); }; //##Documentation //## @brief Sets m_CurrentWorldGeometry virtual void SetCurrentWorldGeometry(const BaseGeometry *geometry); private: //##Documentation //## m_WorldTimeGeometry is set by SetWorldGeometry if the passed BaseGeometry is a //## TimeGeometry (or a sub-class of it). If it contains instances of SlicedGeometry3D, //## m_Slice and m_TimeStep (set via SetSlice and SetTimeStep, respectively) define //## which 2D geometry stored in m_WorldTimeGeometry (if available) //## is used as m_CurrentWorldPlaneGeometry. //## \sa m_CurrentWorldPlaneGeometry TimeGeometry::ConstPointer m_WorldTimeGeometry; //##Documentation //## Pointer to the current 3D-worldgeometry. BaseGeometry::ConstPointer m_CurrentWorldGeometry; //##Documentation //## Pointer to the current 2D-worldgeometry. The 2D-worldgeometry //## describes the maximal area (2D manifold) to be rendered in case we //## are doing 2D-rendering. //## It is const, since we are not allowed to change it (it may be taken //## directly from the geometry of an image-slice and thus it would be //## very strange when suddenly the image-slice changes its geometry). PlaneGeometry::Pointer m_CurrentWorldPlaneGeometry; //##Documentation //## Defines together with m_Slice which 2D geometry stored in m_WorldTimeGeometry //## is used as m_CurrentWorldPlaneGeometry: m_WorldTimeGeometry->GetPlaneGeometry(m_Slice, m_TimeStep). //## \sa m_WorldTimeGeometry unsigned int m_Slice; //##Documentation //## Defines together with m_TimeStep which 2D geometry stored in m_WorldTimeGeometry //## is used as m_CurrentWorldPlaneGeometry: m_WorldTimeGeometry->GetPlaneGeometry(m_Slice, m_TimeStep). //## \sa m_WorldTimeGeometry unsigned int m_TimeStep; //##Documentation //## @brief timestamp of last call of SetWorldGeometry itk::TimeStamp m_CurrentWorldPlaneGeometryUpdateTime; //##Documentation //## @brief timestamp of last change of the current time step itk::TimeStamp m_TimeStepUpdateTime; //##Documentation //## @brief Helper class which establishes connection between Interactors and Dispatcher via a common DataStorage. BindDispatcherInteractor *m_BindDispatcherInteractor; //##Documentation //## @brief Tells if the displayed region should be shifted or rescaled if the render window is resized. bool m_KeepDisplayedRegion; protected: void PrintSelf(std::ostream &os, itk::Indent indent) const override; //##Documentation //## Data object containing the m_CurrentWorldPlaneGeometry defined above. PlaneGeometryData::Pointer m_CurrentWorldPlaneGeometryData; //##Documentation //## DataNode objects containing the m_CurrentWorldPlaneGeometryData defined above. DataNode::Pointer m_CurrentWorldPlaneGeometryNode; //##Documentation //## @brief test only unsigned long m_CurrentWorldPlaneGeometryTransformTime; std::string m_Name; double m_Bounds[6]; bool m_EmptyWorldGeometry; typedef std::set LODEnabledMappersType; /** Number of mappers which are visible and have level-of-detail * rendering enabled */ unsigned int m_NumberOfVisibleLODEnabledMappers; // Local Storage Handling for mappers protected: std::list m_RegisteredLocalStorageHandlers; bool m_ConstrainZoomingAndPanning; public: void RemoveAllLocalStorages(); void RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh); void UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh); }; } // namespace mitk #endif /* BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 */ diff --git a/Modules/Core/include/mitkDataInteractor.h b/Modules/Core/include/mitkDataInteractor.h index 66dcb101d3..b25e689d79 100644 --- a/Modules/Core/include/mitkDataInteractor.h +++ b/Modules/Core/include/mitkDataInteractor.h @@ -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. ============================================================================*/ #ifndef MITKDATAINTERACTOR_H_ #define MITKDATAINTERACTOR_H_ #include #include #include #include #include namespace mitk { class DataNode; - itkEventMacro(DataInteractorEvent, itk::AnyEvent); + itkEventMacroDeclaration(DataInteractorEvent, itk::AnyEvent); - /** Triggered when interaction is started */ - itkEventMacro(StartInteraction, DataInteractorEvent); + /** Triggered when interaction is started */ + itkEventMacroDeclaration(StartInteraction, DataInteractorEvent); - /** Triggered when result is stored in mitk::DataNode */ - itkEventMacro(ResultReady, DataInteractorEvent); + /** Triggered when result is stored in mitk::DataNode */ + itkEventMacroDeclaration(ResultReady, DataInteractorEvent); - enum ProcessEventMode { - REGULAR = 0, - GRABINPUT = 1, - PREFERINPUT = 2, - CONNECTEDMOUSEACTION = 3 - }; + enum ProcessEventMode { + REGULAR = 0, + GRABINPUT = 1, + PREFERINPUT = 2, + CONNECTEDMOUSEACTION = 3 + }; /** * \brief Base class from with interactors that handle DataNodes are to be derived. * * Base class from with interactors that handle DataNodes are to be derived. * Provides an interface that is relevant for the interactor to work together with the dispatcher. * To implement a new interactor overwrite the ConnectActionsAndFunctions to connect the actions. */ class MITKCORE_EXPORT DataInteractor : public EventStateMachine { public: // Predefined internal events/signals static const std::string IntDeactivateMe; static const std::string IntLeaveWidget; static const std::string IntEnterWidget; mitkClassMacro(DataInteractor, EventStateMachine); itkFactorylessNewMacro(Self); itkCloneMacro(Self); DataNode *GetDataNode() const; virtual void SetDataNode(DataNode *dataNode); // TODO: Remove virtual, use DataNodeChanged instead in subclasses int GetLayer() const; ProcessEventMode GetMode() const; protected: DataInteractor(); ~DataInteractor() override; /** * @brief Overwrite this function to connect actions from StateMachine description with functions. * * Following example shows how to connect the 'addpoint' action from the StateMachine XML description using the CONNECT_FUNCTION macro * with the AddPoint() function in the TestInteractor. * @code * void mitk::TestInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addpoint", AddPoint); } * @endcode */ void ConnectActionsAndFunctions() override; /** \brief Is called when a DataNode is initially set or changed * To be implemented by sub-classes for initialization code which require a DataNode. * \note New DataInteractors usually are expected to have the focus, but this only works if they have the highest * Layer, * since empty DataNodes have a layer of -1, the DataNode must be filled here in order to get a layer assigned. * \note Is also called when the DataNode is set to nullptr. */ virtual void DataNodeChanged(); /** * @brief Sends StartInteraction event via the mitk::DataNode */ void virtual NotifyStart(); /** * @brief NotifyResultReady Sends ResultReady event via the mitk::DataNode * * Use to get notfied when the mitk::DataNode is in a ready state for further processing. */ void virtual NotifyResultReady(); private: WeakPointer m_DataNode; }; } #endif diff --git a/Modules/Core/include/mitkDataNode.h b/Modules/Core/include/mitkDataNode.h index 24f5890c5b..74dedd5ae5 100644 --- a/Modules/Core/include/mitkDataNode.h +++ b/Modules/Core/include/mitkDataNode.h @@ -1,597 +1,598 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef DATATREENODE_H_HEADER_INCLUDED_C1E14338 #define DATATREENODE_H_HEADER_INCLUDED_C1E14338 #include "mitkBaseData.h" //#include "mitkMapper.h" #include "mitkDataInteractor.h" #include "mitkIdentifiable.h" #include "mitkIPropertyOwner.h" #include #include #include "mitkColorProperty.h" #include "mitkPropertyList.h" #include "mitkStringProperty.h" //#include "mitkMapper.h" #include "mitkGeometry3D.h" #include "mitkLevelWindow.h" #include #include class vtkLinearTransform; namespace mitk { class BaseRenderer; class Mapper; + /** + * \brief Definition of an itk::Event that is invoked when + * a DataInteractor is set on this DataNode. + */ + itkEventMacroDeclaration(InteractorChangedEvent, itk::AnyEvent); + /** * \brief Class for nodes of the DataTree * * Contains the data (instance of BaseData), a list of mappers, which can * draw the data, a transform (vtkTransform) and a list of properties * (PropertyList). * \ingroup DataManagement * * \todo clean up all the GetProperty methods. There are too many different flavours... Can most probably be reduced * to * bool GetProperty(type&) * * \warning Change in semantics of SetProperty() since Aug 25th 2006. Check your usage of this method if you do * more with properties than just call SetProperty( "key", new SomeProperty("value") ). */ class MITKCORE_EXPORT DataNode : public itk::DataObject, public IPropertyOwner { public: typedef mitk::Geometry3D::Pointer Geometry3DPointer; typedef std::vector> MapperVector; typedef std::map MapOfPropertyLists; typedef std::vector PropertyListKeyNames; typedef std::set GroupTagList; - /** - * \brief Definition of an itk::Event that is invoked when - * a DataInteractor is set on this DataNode. - */ - itkEventMacro(InteractorChangedEvent, itk::AnyEvent) mitkClassMacroItkParent(DataNode, itk::DataObject); itkFactorylessNewMacro(Self); // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; mitk::Mapper *GetMapper(MapperSlotId id) const; /** * \brief Get the data object (instance of BaseData, e.g., an Image) * managed by this DataNode */ BaseData *GetData() const; /** * \brief Get the transformation applied prior to displaying the data as * a vtkTransform * \deprecated use GetData()->GetGeometry()->GetVtkTransform() instead */ vtkLinearTransform *GetVtkTransform(int t = 0) const; /** * \brief Set the data object (instance of BaseData, e.g., an Image) * managed by this DataNode * * Prior set properties are kept if previous data of the node already exists and has the same * type as the new data to be set. Otherwise, the default properties are used. * In case that previous data already exists, the property list of the data node is cleared * before setting new default properties. * * \warning the actor-mode of the vtkInteractor does not work any more, if the transform of the * data-tree-node is connected to the transform of the basedata via vtkTransform->SetInput. */ virtual void SetData(mitk::BaseData *baseData); /** * \brief Set the Interactor. */ virtual void SetDataInteractor(const DataInteractor::Pointer interactor); virtual DataInteractor::Pointer GetDataInteractor() const; mitk::DataNode &operator=(const DataNode &right); mitk::DataNode &operator=(BaseData *right); virtual void SetMapper(MapperSlotId id, mitk::Mapper *mapper); void UpdateOutputInformation() override; void SetRequestedRegionToLargestPossibleRegion() override; bool RequestedRegionIsOutsideOfTheBufferedRegion() override; bool VerifyRequestedRegion() override; void SetRequestedRegion(const itk::DataObject *data) override; void CopyInformation(const itk::DataObject *data) override; /** * \brief The "names" used for (renderer-specific) PropertyLists in GetPropertyList(string). * * All possible values for the "renderer" parameters of * the diverse GetProperty/List() methods. */ PropertyListKeyNames GetPropertyListNames() const; /** * \brief Set the property (instance of BaseProperty) with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-value. * * \warning Change in semantics since Aug 25th 2006. Check your usage of this method if you do * more with properties than just call SetProperty( "key", new SomeProperty("value") ). * * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void SetProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Replace the property (instance of BaseProperty) with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-reference. * * If \a renderer is \a nullptr the property is set in the BaseRenderer-independent * PropertyList of this DataNode. * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void ReplaceProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Add the property (instance of BaseProperty) if it does * not exist (or always if\a overwrite is\a true) * with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent * PropertyList). This is set-by-value. * * For\a overwrite ==\a false the property is\em not changed * if it already exists. For\a overwrite ==\a true the method * is identical to SetProperty. * * \sa SetProperty * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void AddProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); /** * \brief Get the PropertyList of the \a renderer. If \a renderer is \a * nullptr, the BaseRenderer-independent PropertyList of this DataNode * is returned. * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ mitk::PropertyList *GetPropertyList(const mitk::BaseRenderer *renderer = nullptr) const; mitk::PropertyList *GetPropertyList(const std::string &rendererName) const; /** * \brief Add values from another PropertyList. * * Overwrites values in m_PropertyList only when possible (i.e. when types are compatible). * If you want to allow for object type changes (replacing a "visible":BoolProperty with "visible":IntProperty, * set \c replace . * * \param replace true: if \param pList contains a property "visible" of type ColorProperty and our m_PropertyList * also has a "visible" property of a different type (e.g. BoolProperty), change the type, i.e. replace the objects * behind the pointer. * * \sa SetProperty * \sa ReplaceProperty * \sa m_PropertyList */ void ConcatenatePropertyList(PropertyList *pList, bool replace = false); /** * \brief Get the property (instance of BaseProperty) with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * * If \a fallBackOnDataProperties is true, the data property list is queried as a last resort. * * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ mitk::BaseProperty *GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr, bool fallBackOnDataProperties = true) const; /** * \brief Get the property of type T with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ template bool GetProperty(itk::SmartPointer &property, const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property.IsNotNull(); } /** * \brief Get the property of type T with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ template bool GetProperty(T *&property, const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property != nullptr; } /** * \brief Convenience access method for GenericProperty properties * (T being the type of the second parameter) * \return \a true property was found */ template bool GetPropertyValue(const char *propertyKey, T &value, const mitk::BaseRenderer *renderer = nullptr) const { GenericProperty *gp = dynamic_cast *>(GetProperty(propertyKey, renderer)); if (gp != nullptr) { value = gp->GetValue(); return true; } return false; } /// \brief Get a set of all group tags from this node's property list GroupTagList GetGroupTags() const; /** * \brief Convenience access method for bool properties (instances of * BoolProperty) * \return \a true property was found */ bool GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for int properties (instances of * IntProperty) * \return \a true property was found */ bool GetIntProperty(const char *propertyKey, int &intValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for float properties (instances of * FloatProperty) * \return \a true property was found */ bool GetFloatProperty(const char *propertyKey, float &floatValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for double properties (instances of * DoubleProperty) * * If there is no DoubleProperty for the given\c propertyKey argument, the method * looks for a corresponding FloatProperty instance. * * \return \a true property was found */ bool GetDoubleProperty(const char *propertyKey, double &doubleValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for string properties (instances of * StringProperty) * \return \a true property was found */ bool GetStringProperty(const char *propertyKey, std::string &string, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for color properties (instances of * ColorProperty) * \return \a true property was found */ bool GetColor(float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color") const; /** * \brief Convenience access method for level-window properties (instances of * LevelWindowProperty) * \return \a true property was found */ bool GetLevelWindow(mitk::LevelWindow &levelWindow, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "levelwindow") const; /** * \brief set the node as selected */ void SetSelected(bool selected, const mitk::BaseRenderer *renderer = nullptr); /** * \brief set the node as selected * \return \a true node is selected */ bool IsSelected(const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name") * \return \a true property was found */ bool GetName(std::string &nodeName, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "name") const { return GetStringProperty(propertyKey, nodeName, renderer); } /** * \brief Extra convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name"). * * This method does not take the renderer specific * propertylists into account, because the name of an object should never be renderer specific. * \returns a std::string with the name of the object (content of "name" Property). * If there is no "name" Property, an empty string will be returned. */ virtual std::string GetName() const { mitk::StringProperty *sp = dynamic_cast(this->GetProperty("name")); if (sp == nullptr) return ""; return sp->GetValue(); } /** Value constant that is used indicate that node names are not set so far.*/ static std::string NO_NAME_VALUE() { return "No Name!"; } /** * \brief Extra convenience access method to set the name of an object. * * The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name". */ virtual void SetName(const char *name) { if (name == nullptr) return; this->SetProperty("name", StringProperty::New(name)); } /** * \brief Extra convenience access method to set the name of an object. * * The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name". */ virtual void SetName(const std::string name) { this->SetName(name.c_str()); } /** * \brief Convenience access method for visibility properties (instances * of BoolProperty with property-key "visible") * \return \a true property was found * \sa IsVisible */ bool GetVisibility(bool &visible, const mitk::BaseRenderer *renderer, const char *propertyKey = "visible") const { return GetBoolProperty(propertyKey, visible, renderer); } /** * \brief Convenience access method for opacity properties (instances of * FloatProperty) * \return \a true property was found */ bool GetOpacity(float &opacity, const mitk::BaseRenderer *renderer, const char *propertyKey = "opacity") const; /** * \brief Convenience access method for boolean properties (instances * of BoolProperty). Return value is the value of the property. If the property is * not found, the value of \a defaultIsOn is returned. * * Thus, the return value has a different meaning than in the * GetBoolProperty method! * \sa GetBoolProperty */ bool IsOn(const char *propertyKey, const mitk::BaseRenderer *renderer, bool defaultIsOn = true) const { if (propertyKey == nullptr) return defaultIsOn; GetBoolProperty(propertyKey, defaultIsOn, renderer); return defaultIsOn; } /** * \brief Convenience access method for visibility properties (instances * of BoolProperty). Return value is the visibility. Default is * visible==true, i.e., true is returned even if the property (\a * propertyKey) is not found. * * Thus, the return value has a different meaning than in the * GetVisibility method! * \sa GetVisibility * \sa IsOn */ bool IsVisible(const mitk::BaseRenderer *renderer, const char *propertyKey = "visible", bool defaultIsOn = true) const { return IsOn(propertyKey, renderer, defaultIsOn); } /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const mitk::Color &color, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(float red, float green, float blue, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting visibility properties (instances * of BoolProperty) * \param visible If set to true, the data will be rendered. If false, the render will skip this data. * \param renderer Specify a renderer if the visibility shall be specific to a renderer * \param propertyKey Can be used to specify a user defined name of the visibility propery. */ void SetVisibility(bool visible, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "visible"); /** * \brief Convenience method for setting opacity properties (instances of * FloatProperty) */ void SetOpacity(float opacity, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "opacity"); /** * \brief Convenience method for setting level-window properties * (instances of LevelWindowProperty) */ void SetLevelWindow(mitk::LevelWindow levelWindow, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "levelwindow"); /** * \brief Convenience method for setting int properties (instances of * IntProperty) */ void SetIntProperty(const char *propertyKey, int intValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting boolean properties (instances of * BoolProperty) */ void SetBoolProperty(const char *propertyKey, bool boolValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting float properties (instances of * FloatProperty) */ void SetFloatProperty(const char *propertyKey, float floatValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting double properties (instances of * DoubleProperty) */ void SetDoubleProperty(const char *propertyKey, double doubleValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting string properties (instances of * StringProperty) */ void SetStringProperty(const char *propertyKey, const char *string, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Get the timestamp of the last change of the contents of this node or * the referenced BaseData. */ - unsigned long GetMTime() const override; + itk::ModifiedTimeType GetMTime() const override; /** * \brief Get the timestamp of the last change of the reference to the * BaseData. */ unsigned long GetDataReferenceChangedTime() const { return m_DataReferenceChangedTime.GetMTime(); } protected: DataNode(); ~DataNode() override; /// Invoked when the property list was modified. Calls Modified() of the DataNode virtual void PropertyListModified(const itk::Object *caller, const itk::EventObject &event); /// \brief Mapper-slots mutable MapperVector m_Mappers; /** * \brief The data object (instance of BaseData, e.g., an Image) managed * by this DataNode */ BaseData::Pointer m_Data; /** * \brief BaseRenderer-independent PropertyList * * Properties herein can be overwritten specifically for each BaseRenderer * by the BaseRenderer-specific properties defined in m_MapOfPropertyLists. */ PropertyList::Pointer m_PropertyList; /// \brief Map associating each BaseRenderer with its own PropertyList mutable MapOfPropertyLists m_MapOfPropertyLists; DataInteractor::Pointer m_DataInteractor; /// \brief Timestamp of the last change of m_Data itk::TimeStamp m_DataReferenceChangedTime; unsigned long m_PropertyListModifiedObserverTag; }; MITKCORE_EXPORT std::istream &operator>>(std::istream &i, DataNode::Pointer &dtn); MITKCORE_EXPORT std::ostream &operator<<(std::ostream &o, DataNode::Pointer &dtn); } // namespace mitk #endif /* DATATREENODE_H_HEADER_INCLUDED_C1E14338 */ diff --git a/Modules/Core/include/mitkDataStorage.h b/Modules/Core/include/mitkDataStorage.h index a860959317..b884a7b12d 100644 --- a/Modules/Core/include/mitkDataStorage.h +++ b/Modules/Core/include/mitkDataStorage.h @@ -1,442 +1,442 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKDATASTORAGE_H #define MITKDATASTORAGE_H #include "itkObject.h" -#include "itkSimpleFastMutexLock.h" #include "itkVectorContainer.h" #include "mitkDataNode.h" #include "mitkGeometry3D.h" #include "mitkMessage.h" #include #include +#include namespace mitk { class NodePredicateBase; class DataNode; class BaseRenderer; //##Documentation //## @brief Data management class that handles 'was created by' relations //## //## The DataStorage provides data storage and management functionality. //## It handles a 'was created by' relation by associating each data object with a //## set of source objects, that this object was created from. //## Thus, nodes are stored in a noncyclical directed graph data structure. //## If a new node is added to the DataStorage, AddNodeEvent is emitted. //## If a node is removed, RemoveNodeEvent is emitted. //## //## //## \ingroup DataStorage class MITKCORE_EXPORT DataStorage : public itk::Object { public: mitkClassMacroItkParent(DataStorage, itk::Object); //##Documentation //## @brief A Container of objects that is used as a result set of GetSubset() query operations (Set of //SmartPointers // to DataNodes). typedef itk::VectorContainer SetOfObjects; //##Documentation //## @brief Adds a DataNode containing a data object to its internal storage //## //## This Method adds a new data object to the DataStorage. The new object is //## passed in the first parameter. The second parameter is a set //## of source objects, that were used to create this object. The new object will have //## a 'was created from' relation to its source objects. //## the addition of a new object will fire the notification mechanism. //## If the node parameter is nullptr or if the DataNode has already been added, //## an exception will be thrown. virtual void Add(DataNode *node, const DataStorage::SetOfObjects *parents = nullptr) = 0; //##Documentation //## @brief Convenience method to add a node that has one parent //## void Add(DataNode *node, DataNode *parent); //##Documentation //## @brief Removes node from the DataStorage //## virtual void Remove(const DataNode *node) = 0; //##Documentation //## @brief Checks if a node exists in the DataStorage //## virtual bool Exists(const DataNode *node) const = 0; //##Documentation //## @brief Removes a set of nodes from the DataStorage //## void Remove(const DataStorage::SetOfObjects *nodes); //##Documentation //## @brief returns a set of data objects that meet the given condition(s) //## //## GetSubset returns a set of objects with a specific data type that meet the condition(s) //## specified in the condition parameter. Conditions can be //## - data type of the data object //## - is source object of specific object (e.g. all source objects of node x) //## - has property with specific value (e.g. OrganType is Liver) //## - negation of any condition //## - conjunction of a set of conditions //## - disjunction of a set of conditions //## Conditions are implemented as predicates using the Composite Design Pattern //## (see definition of NodePredicateBase for details). //## The method returns a set of SmartPointers to the DataNodes that fulfill the //## conditions. A set of all objects can be retrieved with the GetAll() method; SetOfObjects::ConstPointer GetSubset(const NodePredicateBase *condition) const; //##Documentation //## @brief returns a set of source objects for a given node that meet the given condition(s). //## virtual SetOfObjects::ConstPointer GetSources(const DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectSources = true) const = 0; //##Documentation //## @brief returns a set of derived objects for a given node. //## //## GetDerivations() returns a set of objects that are derived from the DataNode node. //## This means, that node was used to create the returned objects. If the parameter //## onlyDirectDerivations is set to true (default value), only objects that directly have //## node as one of their source objects will be returned. Otherwise, objects that are //## derived from derivations of node are returned too. //## The derived objects can be filtered with a predicate object as described in the GetSubset() //## method by providing a predicate as the condition parameter. virtual SetOfObjects::ConstPointer GetDerivations(const DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectDerivations = true) const = 0; //##Documentation //## @brief returns a set of all data objects that are stored in the data storage //## virtual SetOfObjects::ConstPointer GetAll() const = 0; //##Documentation //## @brief Convenience method to get the first node that matches the predicate condition //## DataNode *GetNode(const NodePredicateBase *condition = nullptr) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## DataNode *GetNamedNode(const char *name) const; //##Documentation //## @brief Convenience method to get the first node with a given name //## DataNode *GetNamedNode(const std::string name) const { return this->GetNamedNode(name.c_str()); } //##Documentation //## @brief Convenience method to get the first node with a given name that is derived from sourceNode //## DataNode *GetNamedDerivedNode(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations = true) const; //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name //## template DataType *GetNamedObject(const char *name) const { if (name == nullptr) return nullptr; DataNode *n = this->GetNamedNode(name); if (n == nullptr) return nullptr; else return dynamic_cast(n->GetData()); } //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name //## template DataType *GetNamedObject(const std::string name) const { return this->GetNamedObject(name.c_str()); } //##Documentation //## @brief Convenience method to get the first data object of a given data type with a given name that is derived // from a specific node //## template DataType *GetNamedDerivedObject(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations = true) const { if (name == nullptr) return nullptr; DataNode *n = this->GetNamedDerivedNode(name, sourceNode, onlyDirectDerivations); if (n == nullptr) return nullptr; else return dynamic_cast(n->GetData()); } //##Documentation //## @brief Returns a list of used grouptags //## const DataNode::GroupTagList GetGroupTags() const; /*ITK Mutex */ - mutable itk::SimpleFastMutexLock m_MutexOne; + mutable std::mutex m_MutexOne; /* Public Events */ typedef Message1 DataStorageEvent; //##Documentation //## @brief AddEvent is emitted whenever a new node has been added to the DataStorage. //## //## Observers should register to this event by calling myDataStorage->AddNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage. //## Observers should unregister by calling myDataStorage->AddNodeEvent.RemoveListener(myObject, //MyObject::MyMethod). //## Note: AddEvents are _not_ emitted if a node is added to DataStorage by adding it to the the underlying //DataTree! // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent AddNodeEvent; //##Documentation //## @brief RemoveEvent is emitted directly before a node is removed from the DataStorage. //## //## Observers should register to this event by calling myDataStorage->RemoveNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been added to the DataStorage. //## Observers should unregister by calling myDataStorage->RemoveNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Note: RemoveEvents are also emitted if a node was removed from the DataStorage by deleting it from the //underlying // DataTree // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent RemoveNodeEvent; //##Documentation //## @brief ChangedEvent is emitted directly after a node was changed. //## //## Observers should register to this event by calling myDataStorage->ChangedNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called every time a new node has been changed. //## Observers should unregister by calling myDataStorage->ChangedNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Internally the DataStorage listens to itk::ModifiedEvents on the nodes and forwards them //## to the listeners of this event. // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent ChangedNodeEvent; //##Documentation //## @brief DeleteNodeEvent is emitted directly before a node is deleted. //## //## Observers should register to this event by calling myDataStorage->DeleteNodeEvent.AddListener(myObject, // MyObject::MyMethod). //## After registering, myObject->MyMethod() will be called when a node is deleted. //## Observers should unregister by calling myDataStorage->DeleteNodeEvent.RemoveListener(myObject, // MyObject::MyMethod). //## Internally the DataStorage listens to itk::DeleteEvents on the nodes and forwards them //## to the listeners of this event. // member variable is not needed to be locked in multi threaded scenarios since the DataStorageEvent is a typedef // for // a Message1 object which is thread safe DataStorageEvent DeleteNodeEvent; DataStorageEvent InteractorChangedNodeEvent; //##Documentation //## @brief Compute the axis-parallel bounding geometry of the input objects //## //## Throws std::invalid_argument exception if input is nullptr //## @param input set of objects of the DataStorage to be included in the bounding geometry //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const SetOfObjects *input, const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr) const; //##Documentation //## @brief Compute the axis-parallel bounding geometry of the data tree //## (bounding box, minimal spacing of the considered nodes, live-span) //## //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeGeometry::ConstPointer ComputeBoundingGeometry3D(const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr) const; //##Documentation //## @brief Compute the axis-parallel bounding geometry of all visible parts of the //## data tree bounding box, minimal spacing of the considered nodes, live-span) //## //## Simply calls ComputeBoundingGeometry3D(it, "visible", renderer, boolPropertyKey). //## it -> an iterator of a data tree structure //## @param renderer the reference to the renderer //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. TimeGeometry::ConstPointer ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer = nullptr, const char *boolPropertyKey = nullptr); //##Documentation //## @brief Compute the bounding box of data tree structure //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey BoundingBox::Pointer ComputeBoundingBox(const char *boolPropertyKey = nullptr, const BaseRenderer *renderer = nullptr, const char *boolPropertyKey2 = nullptr); //##Documentation //## \brief Compute the bounding box of all visible parts of the data tree structure, for general //## rendering or renderer specific visibility property checking //## //## Simply calls ComputeBoundingBox(it, "visible", renderer, boolPropertyKey). //## it -> an iterator of a data tree structure //## @param renderer the reference to the renderer //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the bounding-box calculation. BoundingBox::Pointer ComputeVisibleBoundingBox(const BaseRenderer *renderer = nullptr, const char *boolPropertyKey = nullptr) { return ComputeBoundingBox("visible", renderer, boolPropertyKey); } //##Documentation //## @brief Compute the time-bounds of the contents of a data tree structure //## //## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise, //## all data-objects with infinite live-span are ignored. //## it -> an iterator to a data tree structure //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the time-bounds calculation. //## @param renderer see @a boolPropertyKey //## @param boolPropertyKey2 a second condition that is applied additionally to @a boolPropertyKey TimeBounds ComputeTimeBounds(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2); //##Documentation //## @brief Compute the time-bounds of all visible parts of the data tree structure, for general //## rendering or renderer specific visibility property checking //## //## The methods returns only [-infinity, +infinity], if all data-objects have an infinite live-span. Otherwise, //## all data-objects with infinite live-span are ignored. //## Simply calls ComputeTimeBounds(it, "visible", renderer, boolPropertyKey). //## @param boolPropertyKey if a BoolProperty with this boolPropertyKey exists for a node (for @a renderer) //## and is set to @a false, the node is ignored for the time-bounds calculation. //## @param renderer see @a boolPropertyKey TimeBounds ComputeTimeBounds(const BaseRenderer *renderer, const char *boolPropertyKey) { return ComputeTimeBounds("visible", renderer, boolPropertyKey); } //##Documentation //## @brief Defines whether or not NodeChangedEvent is invoked . //## //## This method can be used to set m_BlockNodeModifiedEvents. //## //## If this flag is true, NodeChangedEvent is not invoked when a //## DataNode is modified. This might be undesired when setting //## many properties on a datanode and you do not want anyone to //## react. void BlockNodeModifiedEvents(bool block); protected: //##Documentation //## @brief EmitAddNodeEvent emits the AddNodeEvent //## //## This method should be called by subclasses to emit the AddNodeEvent void EmitAddNodeEvent(const DataNode *node); //##Documentation //## @brief EmitRemoveNodeEvent emits the RemoveNodeEvent //## //## This method should be called by subclasses to emit the RemoveNodeEvent void EmitRemoveNodeEvent(const DataNode *node); void OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &event); //##Documentation //## @brief OnNodeModified listens to modified events of DataNodes. //## //## The node is hidden behind the caller parameter, which has to be casted first. //## If the cast succeeds the ChangedNodeEvent is emitted with this node. void OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event); //##Documentation //## @brief Adds a Modified-Listener to the given Node. void AddListeners(const DataNode *_Node); //##Documentation //## @brief Removes a Modified-Listener from the given Node. void RemoveListeners(const DataNode *_Node); //##Documentation //## @brief Saves Modified-Observer Tags for each node in order to remove the event listeners again. std::map m_NodeModifiedObserverTags; std::map m_NodeInteractorChangedObserverTags; //##Documentation //## @brief Saves Delete-Observer Tags for each node in order to remove the event listeners again. std::map m_NodeDeleteObserverTags; //##Documentation //## @brief If this class changes nodes itself, set this to TRUE in order //## to suppress NodeChangedEvent to be emitted. bool m_BlockNodeModifiedEvents; DataStorage(); ~DataStorage() override; //##Documentation //## @brief Filters a SetOfObjects by the condition. If no condition is provided, the original set is returned SetOfObjects::ConstPointer FilterSetOfObjects(const SetOfObjects *set, const NodePredicateBase *condition) const; //##Documentation //## @brief Prints the contents of the DataStorage to os. Do not call directly, call ->Print() instead void PrintSelf(std::ostream &os, itk::Indent indent) const override; }; //##Documentation //## @brief returns the topmost visible node of a given list of nodes. //## The function returns a node that is visible and has the highest layer of a set of given nodes. //## The property list, which is used to find the visibility- and layer-property is is specified by the //## given base renderer. //## MITKCORE_EXPORT DataNode::Pointer FindTopmostVisibleNode(const DataStorage::SetOfObjects::ConstPointer nodes, const Point3D worldPosition, const TimePointType timePoint, const BaseRenderer* baseRender); } // namespace mitk #endif // MITKDATASTORAGE_H diff --git a/Modules/Core/include/mitkEqual.h b/Modules/Core/include/mitkEqual.h index ad6d0b9745..af3b911269 100644 --- a/Modules/Core/include/mitkEqual.h +++ b/Modules/Core/include/mitkEqual.h @@ -1,84 +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. ============================================================================*/ /* * mitkEqual.h * * Created on: Apr 14, 2014 * Author: wirkert */ #ifndef MITKEQUAL_H_ #define MITKEQUAL_H_ +#include #include #include "mitkLogMacros.h" #include "mitkNumericConstants.h" namespace mitk { /** * Helper method to check if the difference is bigger or equal to a given epsilon * * @param diff the difference to be checked against the epsilon * @param epsilon The absolute difference needs to be smaller than this. * @return true if abs(diff) >= eps */ template inline bool DifferenceBiggerOrEqualEps(DifferenceType diff, mitk::ScalarType epsilon = mitk::eps) { - return fabs(diff) >= epsilon; + return std::fabs(diff) >= epsilon; } /** * outputs elem1, elem2 and eps in case verbose and !isEqual. * Elem can e.g. be a mitk::Vector or an mitk::Point. * * @param elem1 first element to be output * @param elem2 second * @param eps the epsilon which their difference was bigger than * @param verbose tells the function if something shall be output * @param isEqual function will only output something if the two elements are not equal */ template inline void ConditionalOutputOfDifference( ElementToOutput1 elem1, ElementToOutput2 elem2, mitk::ScalarType eps, bool verbose, bool isEqual) { if (verbose && !isEqual) { MITK_INFO << typeid(ElementToOutput1).name() << " and " << typeid(ElementToOutput2).name() << " not equal. Lefthandside " << std::setprecision(12) << elem1 << " - Righthandside " << elem2 << " - epsilon " << eps; } } /** * @ingroup MITKTestingAPI * * @param scalar1 Scalar value to compare. * @param scalar2 Scalar value to compare. * @param eps Tolerance for floating point comparison. * @param verbose Flag indicating detailed console output. * @return True if scalars are equal. */ inline bool Equal(ScalarType scalar1, ScalarType scalar2, ScalarType eps = mitk::eps, bool verbose = false) { bool isEqual(!DifferenceBiggerOrEqualEps(scalar1 - scalar2, eps)); ConditionalOutputOfDifference(scalar1, scalar2, eps, verbose, isEqual); return isEqual; } } #endif /* MITKEQUAL_H_ */ diff --git a/Modules/Core/include/mitkExtractSliceFilter2.h b/Modules/Core/include/mitkExtractSliceFilter2.h index 60303f435c..5366ccbc95 100644 --- a/Modules/Core/include/mitkExtractSliceFilter2.h +++ b/Modules/Core/include/mitkExtractSliceFilter2.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 mitkExtractSliceFilter2_h #define mitkExtractSliceFilter2_h #include #include #include namespace mitk { /** \brief Extract an arbitrarily oriented 2-d image from a 3-d image. * * Use ExtractSliceFilter2::SetOutputGeometry to specify the orientation of * the 2-d output image. * * If a pixel of the 2-d output image isn't located within the bounds of the * 3-d input image, it is set to the lowest possible pixel value. * * Cubic interpolation is considerably slow on the first update for a newly * set input image. Subsequent filter updates with cubic interpolation are * faster by several orders of magnitude as long as the input image was * neither changed nor modified. * * This filter is completely based on ITK compared to the VTK-based * mitk::ExtractSliceFilter. It is more robust, easy to use, and produces * an mitk::Image with valid geometry. Generally it is not as fast as * mitk::ExtractSliceFilter, though. */ class MITKCORE_EXPORT ExtractSliceFilter2 final : public ImageToImageFilter { public: enum Interpolator { NearestNeighbor, Linear, Cubic }; mitkClassMacro(ExtractSliceFilter2, ImageToImageFilter); itkFactorylessNewMacro(Self); void SetInput(const InputImageType* image) override; void SetInput(unsigned int index, const InputImageType* image) override; const PlaneGeometry* GetOutputGeometry() const; void SetOutputGeometry(PlaneGeometry::Pointer outputGeometry); Interpolator GetInterpolator() const; void SetInterpolator(Interpolator interpolator); private: using Superclass::SetInput; ExtractSliceFilter2(); ~ExtractSliceFilter2() override; void AllocateOutputs() override; // void BeforeThreadedGenerateData() override; // void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType threadId) override; void GenerateData() override; - void VerifyInputInformation() override; + void VerifyInputInformation() const override; struct Impl; Impl* m_Impl; }; } #endif diff --git a/Modules/Core/include/mitkIOUtil.h b/Modules/Core/include/mitkIOUtil.h index 946016eb9c..24f878efa2 100644 --- a/Modules/Core/include/mitkIOUtil.h +++ b/Modules/Core/include/mitkIOUtil.h @@ -1,456 +1,434 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKIOUTIL_H #define MITKIOUTIL_H #include #include #include #include #include #include #include #include #include #include namespace us { class ModuleResource; } namespace mitk { /** * \ingroup IO * * \brief A utility class to load and save data from/to the local file system. * * \see QmitkIOUtil */ class MITKCORE_EXPORT IOUtil { public: /**Struct that containes information regarding the current loading process. (e.g. Path that should be loaded, all found readers for the load path,...). It is set be IOUtil and used to pass information via the option callback in load operations. */ struct MITKCORE_EXPORT LoadInfo { LoadInfo(const std::string &path); std::string m_Path; std::vector m_Output; FileReaderSelector m_ReaderSelector; bool m_Cancel; }; /**Struct that is the base class for option callbacks used in load operations. The callback is used by IOUtil, if more than one suitable reader was found or the a reader containes options that can be set. The callback allows to change option settings and select the reader that should be used (via loadInfo). */ struct MITKCORE_EXPORT ReaderOptionsFunctorBase { virtual bool operator()(LoadInfo &loadInfo) const = 0; }; struct MITKCORE_EXPORT SaveInfo { SaveInfo(const BaseData *baseData, const MimeType &mimeType, const std::string &path); bool operator<(const SaveInfo &other) const; /// The BaseData object to save. const BaseData *m_BaseData; /// Contains a set of IFileWriter objects. FileWriterSelector m_WriterSelector; /// The selected mime-type, used to restrict results from FileWriterSelector. MimeType m_MimeType; /// The path to write the BaseData object to. std::string m_Path; /// Flag indicating if sub-sequent save operations are to be canceled. bool m_Cancel; }; /**Struct that is the base class for option callbacks used in save operations. The callback is used by IOUtil, if more than one suitable writer was found or the a writer containes options that can be set. The callback allows to change option settings and select the writer that should be used (via saveInfo). */ struct MITKCORE_EXPORT WriterOptionsFunctorBase { virtual bool operator()(SaveInfo &saveInfo) const = 0; }; /** * Get the file system path where the running executable is located. * * @return The location of the currently running executable, without the filename. */ static std::string GetProgramPath(); /** * Get the default temporary path. * * @return The default path for temporary data. */ static std::string GetTempPath(); /** * Returns the Directory Seperator for the current OS. * * @return the Directory Seperator for the current OS, i.e. "\\" for Windows and "/" otherwise. */ static char GetDirectorySeparator(); /** * Create and open a temporary file. * * This method generates a unique temporary filename from \c templateName, creates * and opens the file using the output stream \c tmpStream and returns the name of * the newly create file. * * The \c templateName argument must contain six consective 'X' characters ("XXXXXX") * and these are replaced with a string that makes the filename unique. * * The file is created with read and write permissions for owner only. * * @param tmpStream The output stream for writing to the temporary file. * @param templateName An optional template for the filename. * @param path An optional path where the temporary file should be created. Defaults * to the default temp path as returned by GetTempPath(). * @return The filename of the created temporary file. * * @throw mitk::Exception if the temporary file could not be created. */ static std::string CreateTemporaryFile(std::ofstream &tmpStream, const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * Create and open a temporary file. * * This method generates a unique temporary filename from \c templateName, creates * and opens the file using the output stream \c tmpStream and the specified open * mode \c mode and returns the name of the newly create file. The open mode is always * OR'd with std::ios_base::out | std::ios_base::trunc. * * The \c templateName argument must contain six consective 'X' characters ("XXXXXX") * and these are replaced with a string that makes the filename unique. * * The file is created with read and write permissions for owner only. * * @param tmpStream The output stream for writing to the temporary file. * @param mode The open mode for the temporary file stream. * @param templateName An optional template for the filename. * @param path An optional path where the temporary file should be created. Defaults * to the default temp path as returned by GetTempPath(). * @return The filename of the created temporary file. * * @throw mitk::Exception if the temporary file could not be created. */ static std::string CreateTemporaryFile(std::ofstream &tmpStream, std::ios_base::openmode mode, const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * Creates an empty temporary file. * * This method generates a unique temporary filename from \c templateName and creates * this file. * * The file is created with read and write permissions for owner only. * * --- * This version is potentially unsafe because the created temporary file is not kept open * and could be used by another process between calling this method and opening the returned * file path for reading or writing. * --- * * @return The filename of the created temporary file. * @param templateName An optional template for the filename. * @param path An optional path where the temporary file should be created. Defaults * to the default temp path as returned by GetTempPath(). * @throw mitk::Exception if the temporary file could not be created. */ static std::string CreateTemporaryFile(const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * Create a temporary directory. * * This method generates a uniquely named temporary directory from \c templateName. * The last set of six consecutive 'X' characters in \c templateName is replaced * with a string that makes the directory name unique. * * The directory is created with read, write and executable permissions for owner only. * * @param templateName An optional template for the directory name. * @param path An optional path where the temporary directory should be created. Defaults * to the default temp path as returned by GetTempPath(). * @return The filename of the created temporary file. * * @throw mitk::Exception if the temporary directory could not be created. */ static std::string CreateTemporaryDirectory(const std::string &templateName = "XXXXXX", std::string path = std::string()); /** * @brief Load a file into the given DataStorage. * * This method calls Load(const std::vector&, DataStorage&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param storage A DataStorage object to which the loaded data will be added. * @param optionsCallback Pointer to a callback instance. The callback is used by * the load operation if more the suitable reader was found or the reader has options * that can be set. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static DataStorage::SetOfObjects::Pointer Load(const std::string &path, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback = nullptr); /** * @brief Load a file into the given DataStorage given user defined IFileReader::Options. * * This method calls Load(const std::vector&, DataStorage&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param options IFileReader option instance that should be used if selected reader * has options. * @param storage A DataStorage object to which the loaded data will be added. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static DataStorage::SetOfObjects::Pointer Load(const std::string &path, const IFileReader::Options &options, DataStorage &storage); /** * @brief Load a file and return the loaded data. * * This method calls Load(const std::vector&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param optionsCallback Pointer to a callback instance. The callback is used by * the load operation if more the suitable reader was found or the reader has options * that can be set. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static std::vector Load(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback = nullptr); template static typename T::Pointer Load(const std::string& path, const ReaderOptionsFunctorBase *optionsCallback = nullptr) { return dynamic_cast(Load(path, optionsCallback).at(0).GetPointer()); } /** * @brief Load a file and return the loaded data. * * This method calls Load(const std::vector&) with a * one-element vector. * * @param path The absolute file name including the file extension. * @param options IFileReader option instance that should be used if selected reader * has options. * @return The set of added DataNode objects. * @throws mitk::Exception if \c path could not be loaded. * * @sa Load(const std::vector&, DataStorage&) */ static std::vector Load(const std::string &path, const IFileReader::Options &options); template static typename T::Pointer Load(const std::string& path, const IFileReader::Options &options) { return dynamic_cast(Load(path, options).at(0).GetPointer()); } /** * @brief Loads a list of file paths into the given DataStorage. * * If an entry in \c paths cannot be loaded, this method will continue to load * the remaining entries into \c storage and throw an exception afterwards. * * @param paths A list of absolute file names including the file extension. * @param storage A DataStorage object to which the loaded data will be added. * @param optionsCallback Pointer to a callback instance. The callback is used by * the load operation if more the suitable reader was found or the reader has options * that can be set. * @return The set of added DataNode objects. * @throws mitk::Exception if an entry in \c paths could not be loaded. */ static DataStorage::SetOfObjects::Pointer Load(const std::vector &paths, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback = nullptr); static std::vector Load(const std::vector &paths, const ReaderOptionsFunctorBase *optionsCallback = nullptr); /** * @brief Loads the contents of a us::ModuleResource and returns the corresponding mitk::BaseData * @param usResource a ModuleResource, representing a BaseData object * @param mode Optional parameter to set the openmode of the stream * @return The set of loaded BaseData objects. \c Should contain either one or zero elements, since a resource * stream * respresents one object. * @throws mitk::Exception if no reader was found for the stream. */ static std::vector Load(const us::ModuleResource &usResource, std::ios_base::openmode mode = std::ios_base::in); template static typename T::Pointer Load(const us::ModuleResource &usResource, std::ios_base::openmode mode = std::ios_base::in) { return dynamic_cast(Load(usResource, mode).at(0).GetPointer()); } /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param path The path to the image including file name and and optional file extension. * If no extension is set, the default extension and mime-type for the * BaseData type of \c data is used. * @param setPathProperty * @throws mitk::Exception if no writer for \c data is available or the writer * is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &path, bool setPathProperty = false); /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param path The path to the image including file name and an optional file extension. * If no extension is set, the default extension and mime-type for the * BaseData type of \c data is used. * @param options The IFileWriter options to use for the selected writer. * @param setPathProperty * @throws mitk::Exception if no writer for \c data is available or the writer * is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &path, const IFileWriter::Options &options, bool setPathProperty = false); /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param mimeType The mime-type to use for writing \c data. * @param path The path to the image including file name and an optional file extension. * @param addExtension If \c true, an extension according to the given \c mimeType * is added to \c path if it does not contain one. If \c path already contains * a file name extension, it is not checked for compatibility with \c mimeType. * @param setPathProperty * * @throws mitk::Exception if no writer for the combination of \c data and \c mimeType is * available or the writer is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &mimeType, const std::string &path, bool addExtension = true, bool setPathProperty = false); /** * @brief Save a mitk::BaseData instance. * @param data The data to save. * @param mimeType The mime-type to use for writing \c data. * @param path The path to the image including file name and an optional file extension. * @param options Configuration data for the used IFileWriter instance. * @param addExtension If \c true, an extension according to the given \c mimeType * is added to \c path if it does not contain one. If \c path already contains * a file name extension, it is not checked for compatibility with \c mimeType. * @param setPathProperty * * @throws mitk::Exception if no writer for the combination of \c data and \c mimeType is * available or the writer is not able to write the image. */ static void Save(const mitk::BaseData *data, const std::string &mimeType, const std::string &path, const mitk::IFileWriter::Options &options, bool addExtension = true, bool setPathProperty = false); /** * @brief Use SaveInfo objects to save BaseData instances. * * This is a low-level method for directly working with SaveInfo objects. Usually, * the Save() methods taking a BaseData object as an argument are more appropriate. * * @param saveInfos A list of SaveInfo objects for saving contained BaseData objects. * @param setPathProperty * * @see Save(const mitk::BaseData*, const std::string&) */ static void Save(std::vector &saveInfos, bool setPathProperty = false); - /** - * @brief Convert a string encoded with the current code page to an UTF-8 encoded string (Windows) - * - * The conversion happens on Windows only. On all other platforms, the input string - * is returned unmodified as it is assumed to be UTF-8 encoded already. - * - * If the conversion fails, a warning is printed and the input string is returned - * instead. This matches the behavior before this method was introduced. - */ - static std::string Local8BitToUtf8(const std::string& local8BitStr); - - /** - * @brief Convert a UTF-8 encoded string to a string encoded with the current code page (Windows) - * - * The conversion happens on Windows only. On all other platforms, the input string - * is returned unmodified as strings are assumed to be always UTF-8 encoded by default. - * - * If the conversion fails, a warning is printed and the input string is returned - * instead. This matches the behavior before this method was introduced. - */ - static std::string Utf8ToLocal8Bit(const std::string& utf8Str); - protected: static std::string Load(std::vector &loadInfos, DataStorage::SetOfObjects *nodeResult, DataStorage *ds, const ReaderOptionsFunctorBase *optionsCallback); static std::string Save(const BaseData *data, const std::string &mimeType, const std::string &path, WriterOptionsFunctorBase *optionsCallback, bool addExtension, bool setPathProperty); static std::string Save(std::vector &saveInfos, WriterOptionsFunctorBase *optionsCallback, bool setPathProperty); private: struct Impl; }; } #endif // MITKIOUTIL_H diff --git a/Modules/Core/include/mitkImage.h b/Modules/Core/include/mitkImage.h index c8b6f03464..a568fb65b8 100644 --- a/Modules/Core/include/mitkImage.h +++ b/Modules/Core/include/mitkImage.h @@ -1,643 +1,643 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #define MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 #include "mitkBaseData.h" #include "mitkImageAccessorBase.h" #include "mitkImageDataItem.h" #include "mitkImageDescriptor.h" #include "mitkImageVtkAccessor.h" #include "mitkLevelWindow.h" #include "mitkPlaneGeometry.h" #include "mitkSlicedData.h" #include #include #ifndef __itkHistogram_h #include #endif class vtkImageData; namespace itk { template class MutexLockHolder; } namespace mitk { class SubImageSelector; class ImageTimeSelector; class ImageStatisticsHolder; /** * @brief Image class for storing images * * Can be asked for header information, the data vector, or vtkImageData objects. * If not the complete data is required, the appropriate SubImageSelector class * should be used for access. * Image organizes sets of slices (s x 2D), volumes (t x 3D) and channels (n * x ND). Channels are for different kind of data, e.g., morphology in * channel 0, velocities in channel 1. All channels must have the same Geometry! In * particular, the dimensions of all channels are the same, only the pixel-type * may differ between channels. * * For importing ITK images use of mitk::ITKImageImport is recommended, see * \ref Adaptor. * * For ITK v3.8 and older: Converting coordinates from the ITK physical * coordinate system (which does not support rotated images) to the MITK world * coordinate system should be performed via the BaseGeometry of the Image, see * BaseGeometry::WorldToItkPhysicalPoint. * * For more information, see \ref MitkImagePage . * @ingroup Data */ class MITKCORE_EXPORT Image : public SlicedData { friend class SubImageSelector; friend class ImageAccessorBase; friend class ImageVtkAccessor; friend class ImageVtkReadAccessor; friend class ImageVtkWriteAccessor; friend class ImageReadAccessor; friend class ImageWriteAccessor; public: mitkClassMacro(Image, SlicedData); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** Smart Pointer type to a ImageDataItem. */ typedef itk::SmartPointer ImageDataItemPointer; typedef itk::Statistics::Histogram HistogramType; typedef mitk::ImageStatisticsHolder *StatisticsHolderPointer; /** This enum is evaluated when setting new data to an image. */ enum ImportMemoryManagementType { CopyMemory, /**< Data to be set is copied and assigned to a new memory block. Data memory block will be freed on deletion of mitk::Image. */ ManageMemory, /**< Data to be set will be referenced, and Data memory block will be freed on deletion of mitk::Image. */ ReferenceMemory, /**< Data to be set will be referenced, but Data memory block will not be freed on deletion of mitk::Image. */ DontManageMemory = ReferenceMemory }; /** * @brief Vector container of SmartPointers to ImageDataItems; * Class is only for internal usage to allow convenient access to all slices over iterators; * See documentation of ImageDataItem for details. */ typedef std::vector ImageDataItemPointerArray; public: /** * @brief Returns the PixelType of channel @a n. */ const mitk::PixelType GetPixelType(int n = 0) const; /** * @brief Get dimension of the image */ unsigned int GetDimension() const; /** * @brief Get the size of dimension @a i (e.g., i=0 results in the number of pixels in x-direction). * * @sa GetDimensions() */ unsigned int GetDimension(int i) const; public: /** * @brief Get a volume at a specific time @a t of channel @a n as a vtkImageData. */ virtual vtkImageData *GetVtkImageData(int t = 0, int n = 0); virtual const vtkImageData *GetVtkImageData(int t = 0, int n = 0) const; /** * @brief Check whether slice @a s at time @a t in channel @a n is set */ bool IsSliceSet(int s = 0, int t = 0, int n = 0) const override; /** * @brief Check whether volume at time @a t in channel @a n is set */ bool IsVolumeSet(int t = 0, int n = 0) const override; /** * @brief Check whether the channel @a n is set */ bool IsChannelSet(int n = 0) const override; /** * @brief Set @a data as slice @a s at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a slice (at least is not smaller than a slice), since there is * no chance to check this. * * The data is copied to an array managed by the image. If the image shall * reference the data, use SetImportSlice with ImportMemoryManagementType * set to ReferenceMemory. For importing ITK images use of mitk:: * ITKImageImport is recommended. * @sa SetPicSlice, SetImportSlice, SetImportVolume */ virtual bool SetSlice(const void *data, int s = 0, int t = 0, int n = 0); /** * @brief Set @a data as volume at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a volume (at least is not smaller than a volume), since there is * no chance to check this. * * The data is copied to an array managed by the image. If the image shall * reference the data, use SetImportVolume with ImportMemoryManagementType * set to ReferenceMemory. For importing ITK images use of mitk:: * ITKImageImport is recommended. * @sa SetPicVolume, SetImportVolume */ virtual bool SetVolume(const void *data, int t = 0, int n = 0); /** * @brief Set @a data in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a channel (at least is not smaller than a channel), since there is * no chance to check this. * * The data is copied to an array managed by the image. If the image shall * reference the data, use SetImportChannel with ImportMemoryManagementType * set to ReferenceMemory. For importing ITK images use of mitk:: * ITKImageImport is recommended. * @sa SetPicChannel, SetImportChannel */ virtual bool SetChannel(const void *data, int n = 0); /** * @brief Set @a data as slice @a s at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a slice (at least is not smaller than a slice), since there is * no chance to check this. * * The data is managed according to the parameter \a importMemoryManagement. * @sa SetPicSlice */ virtual bool SetImportSlice( void *data, int s = 0, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * @brief Set @a data as volume at time @a t in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a volume (at least is not smaller than a volume), since there is * no chance to check this. * * The data is managed according to the parameter \a importMemoryManagement. * @sa SetPicVolume */ virtual bool SetImportVolume(void *data, int t = 0, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory); virtual bool SetImportVolume(const void *const_data, int t = 0, int n = 0); /** * @brief Set @a data in channel @a n. It is in * the responsibility of the caller to ensure that the data vector @a data * is really a channel (at least is not smaller than a channel), since there is * no chance to check this. * * The data is managed according to the parameter \a importMemoryManagement. * @sa SetPicChannel */ virtual bool SetImportChannel(void *data, int n = 0, ImportMemoryManagementType importMemoryManagement = CopyMemory); /** * initialize new (or re-initialize) image information * @warning Initialize() by pic assumes a plane, evenly spaced geometry starting at (0,0,0). */ virtual void Initialize(const mitk::PixelType &type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels = 1); /** * initialize new (or re-initialize) image information by a BaseGeometry * * \param type * \param geometry * \param channels * @param tDim defines the number of time steps for which the Image should be initialized */ virtual void Initialize(const mitk::PixelType &type, const mitk::BaseGeometry &geometry, unsigned int channels = 1, int tDim = 1); /** * \brief Initialize new (or re-initialize) image information by a TimeGeometry * * \param type * \param geometry * \param channels * \param tDim override time dimension if the value is bigger than 0 (Default -1) */ virtual void Initialize(const mitk::PixelType &type, const mitk::TimeGeometry &geometry, unsigned int channels = 1, int tDim = -1); /** * initialize new (or re-initialize) image information by a PlaneGeometry and number of slices * * Initializes the bounding box according to the width/height of the * PlaneGeometry and @a sDim via SlicedGeometry3D::InitializeEvenlySpaced. * The spacing is calculated from the PlaneGeometry. * \sa SlicedGeometry3D::InitializeEvenlySpaced */ virtual void Initialize(const mitk::PixelType &type, int sDim, const mitk::PlaneGeometry &geometry2d, unsigned int channels = 1, int tDim = 1); /** * initialize new (or re-initialize) image information by another * mitk-image. * Only the header is used, not the data vector! */ virtual void Initialize(const mitk::Image *image); virtual void Initialize(const mitk::ImageDescriptor::Pointer inDesc); /** * initialize new (or re-initialize) image information by @a vtkimagedata, * a vtk-image. * Only the header is used, not the data vector! Use * SetVolume(vtkimage->GetScalarPointer()) to set the data vector. * * @param vtkimagedata * @param channels * @param tDim override time dimension in @a vtkimagedata (if >0 and <) * @param sDim override z-space dimension in @a vtkimagedata (if >0 and <) * @param pDim override y-space dimension in @a vtkimagedata (if >0 and <) */ virtual void Initialize(vtkImageData *vtkimagedata, int channels = 1, int tDim = -1, int sDim = -1, int pDim = -1); /** * initialize new (or re-initialize) image information by @a itkimage, * a templated itk-image. * Only the header is used, not the data vector! Use * SetVolume(itkimage->GetBufferPointer()) to set the data vector. * * @param itkimage * @param channels * @param tDim override time dimension in @a itkimage (if >0 and <) * @param sDim override z-space dimension in @a itkimage (if >0 and <) */ template void InitializeByItk(const itkImageType *itkimage, int channels = 1, int tDim = -1, int sDim = -1) { if (itkimage == nullptr) return; MITK_DEBUG << "Initializing MITK image from ITK image."; // build array with dimensions in each direction with at least 4 entries m_Dimension = itkimage->GetImageDimension(); unsigned int i, *tmpDimensions = new unsigned int[m_Dimension > 4 ? m_Dimension : 4]; for (i = 0; i < m_Dimension; ++i) tmpDimensions[i] = itkimage->GetLargestPossibleRegion().GetSize().GetSize()[i]; if (m_Dimension < 4) { unsigned int *p; for (i = 0, p = tmpDimensions + m_Dimension; i < 4 - m_Dimension; ++i, ++p) *p = 1; } // overwrite number of slices if sDim is set if ((m_Dimension > 2) && (sDim >= 0)) tmpDimensions[2] = sDim; // overwrite number of time points if tDim is set if ((m_Dimension > 3) && (tDim >= 0)) tmpDimensions[3] = tDim; // rough initialization of Image // mitk::PixelType importType = ImportItkPixelType( itkimage::PixelType ); Initialize( MakePixelType(itkimage->GetNumberOfComponentsPerPixel()), m_Dimension, tmpDimensions, channels); const typename itkImageType::SpacingType &itkspacing = itkimage->GetSpacing(); MITK_DEBUG << "ITK spacing " << itkspacing; // access spacing of itk::Image Vector3D spacing; FillVector3D(spacing, itkspacing[0], 1.0, 1.0); if (m_Dimension >= 2) spacing[1] = itkspacing[1]; if (m_Dimension >= 3) spacing[2] = itkspacing[2]; // access origin of itk::Image Point3D origin; const typename itkImageType::PointType &itkorigin = itkimage->GetOrigin(); MITK_DEBUG << "ITK origin " << itkorigin; FillVector3D(origin, itkorigin[0], 0.0, 0.0); if (m_Dimension >= 2) origin[1] = itkorigin[1]; if (m_Dimension >= 3) origin[2] = itkorigin[2]; // access direction of itk::Imagm_PixelType = new mitk::PixelType(type);e and include spacing const typename itkImageType::DirectionType &itkdirection = itkimage->GetDirection(); MITK_DEBUG << "ITK direction " << itkdirection; mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (m_Dimension >= 3 ? 3 : m_Dimension); // check if spacing has no zero entry and itkdirection has no zero columns bool itkdirectionOk = true; mitk::ScalarType columnSum; for (j = 0; j < itkDimMax3; ++j) { columnSum = 0.0; for (i = 0; i < itkDimMax3; ++i) { columnSum += fabs(itkdirection[i][j]); } if (columnSum < mitk::eps) { itkdirectionOk = false; } if ((spacing[j] < -mitk::eps) // (normally sized) negative value && (j == 2) && (m_Dimensions[2] == 1)) { // Negative spacings can occur when reading single DICOM slices with ITK via GDCMIO // In these cases spacing is not determind by ITK correctly (because it distinguishes correctly // between slice thickness and inter slice distance -- slice distance is meaningless for // single slices). // I experienced that ITK produced something meaningful nonetheless because is is // evaluating the tag "(0018,0088) Spacing between slices" as a fallback. This tag is not // reliable (http://www.itk.org/pipermail/insight-users/2005-September/014711.html) // but gives at least a hint. // In real world cases I experienced that this tag contained the correct inter slice distance // with a negative sign, so we just invert such negative spacings. MITK_WARN << "Illegal value of itk::Image::GetSpacing()[" << j << "]=" << spacing[j] << ". Using inverted value " << -spacing[j]; spacing[j] = -spacing[j]; } else if (spacing[j] < mitk::eps) // value near zero { MITK_ERROR << "Illegal value of itk::Image::GetSpacing()[" << j << "]=" << spacing[j] << ". Using 1.0 instead."; spacing[j] = 1.0; } } if (itkdirectionOk == false) { MITK_ERROR << "Illegal matrix returned by itk::Image::GetDirection():" << itkdirection << " Using identity instead."; for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) if (i == j) matrix[i][j] = spacing[j]; else matrix[i][j] = 0.0; } else { for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = itkdirection[i][j] * spacing[j]; } // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = static_cast(GetSlicedGeometry(0)->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, m_Dimensions[2]); slicedGeometry->SetSpacing(spacing); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); // clean-up delete[] tmpDimensions; this->Initialize(); } /** * @brief Check whether slice @a s at time @a t in channel @a n is valid, i.e., * is (or can be) inside of the image */ virtual bool IsValidSlice(int s = 0, int t = 0, int n = 0) const; /** * @brief Check whether volume at time @a t in channel @a n is valid, i.e., * is (or can be) inside of the image */ virtual bool IsValidVolume(int t = 0, int n = 0) const; /** * @brief Check whether the channel @a n is valid, i.e., * is (or can be) inside of the image */ virtual bool IsValidChannel(int n = 0) const; /** * @brief Returns true if an image is rotated, i.e. its geometry's * transformation matrix has nonzero elements besides the diagonal. * Non-diagonal elements are checked if larger then 1/1000 of the matrix' trace. */ bool IsRotated() const; /** * @brief Get the sizes of all dimensions as an integer-array. * * @sa GetDimension(int i); */ unsigned int *GetDimensions() const; ImageDescriptor::Pointer GetImageDescriptor() const { return m_ImageDescriptor; } ChannelDescriptor GetChannelDescriptor(int id = 0) const { return m_ImageDescriptor->GetChannelDescriptor(id); } /** \brief Sets a geometry to an image. */ void SetGeometry(BaseGeometry *aGeometry3D) override; /** * @warning for internal use only */ virtual ImageDataItemPointer GetSliceData(int s = 0, int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; /** * @warning for internal use only */ virtual ImageDataItemPointer GetVolumeData(int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; /** * @warning for internal use only */ virtual ImageDataItemPointer GetChannelData(int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; /** \brief Returns a pointer to the ImageStatisticsHolder object that holds all statistics information for the image. All Get-methods for statistics properties formerly accessible directly from an Image object are now moved to the new \a ImageStatisticsHolder object. */ StatisticsHolderPointer GetStatistics() const { return m_ImageStatistics; } protected: mitkCloneMacro(Self); - typedef itk::MutexLockHolder MutexHolder; + typedef std::lock_guard MutexHolder; int GetSliceIndex(int s = 0, int t = 0, int n = 0) const; int GetVolumeIndex(int t = 0, int n = 0) const; void ComputeOffsetTable(); virtual bool IsValidTimeStep(int t) const; void Expand(unsigned int timeSteps) override; virtual ImageDataItemPointer AllocateSliceData( int s = 0, int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; virtual ImageDataItemPointer AllocateVolumeData( int t = 0, int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; virtual ImageDataItemPointer AllocateChannelData( int n = 0, void *data = nullptr, ImportMemoryManagementType importMemoryManagement = CopyMemory) const; Image(); Image(const Image &other); ~Image() override; void Clear() override; /** @warning Has to be called by every Initialize method! */ void Initialize() override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; mutable ImageDataItemPointerArray m_Channels; mutable ImageDataItemPointerArray m_Volumes; mutable ImageDataItemPointerArray m_Slices; - mutable itk::SimpleFastMutexLock m_ImageDataArraysLock; + mutable std::mutex m_ImageDataArraysLock; unsigned int m_Dimension; unsigned int *m_Dimensions; ImageDescriptor::Pointer m_ImageDescriptor; size_t *m_OffsetTable; ImageDataItemPointer m_CompleteData; // Image statistics Holder replaces the former implementation directly inside this class friend class ImageStatisticsHolder; StatisticsHolderPointer m_ImageStatistics; private: ImageDataItemPointer GetSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer GetVolumeData_unlocked(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer GetChannelData_unlocked(int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer AllocateSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer AllocateVolumeData_unlocked(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const; ImageDataItemPointer AllocateChannelData_unlocked(int n, void *data, ImportMemoryManagementType importMemoryManagement) const; bool IsSliceSet_unlocked(int s, int t, int n) const; bool IsVolumeSet_unlocked(int t, int n) const; bool IsChannelSet_unlocked(int n) const; /** Stores all existing ImageReadAccessors */ mutable std::vector m_Readers; /** Stores all existing ImageWriteAccessors */ mutable std::vector m_Writers; /** Stores all existing ImageVtkAccessors */ mutable std::vector m_VtkReaders; /** A mutex, which needs to be locked to manage m_Readers and m_Writers */ - itk::SimpleFastMutexLock m_ReadWriteLock; + mutable std::mutex m_ReadWriteLock; /** A mutex, which needs to be locked to manage m_VtkReaders */ - itk::SimpleFastMutexLock m_VtkReadersLock; + mutable std::mutex m_VtkReadersLock; }; /** * @brief Equal A function comparing two images for beeing equal in meta- and imagedata * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - dimension of the images * - size of the images * - pixel type * - pixel values : pixel values are expected to be identical at each position ( for other options see * mitk::CompareImageFilter ) * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKCORE_EXPORT bool Equal(const mitk::Image &leftHandSide, const mitk::Image &rightHandSide, ScalarType eps, bool verbose); } // namespace mitk #endif /* MITKIMAGE_H_HEADER_INCLUDED_C1C2FCD2 */ diff --git a/Modules/Core/include/mitkImageAccessorBase.h b/Modules/Core/include/mitkImageAccessorBase.h index e80978f435..a0419cc23d 100644 --- a/Modules/Core/include/mitkImageAccessorBase.h +++ b/Modules/Core/include/mitkImageAccessorBase.h @@ -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. ============================================================================*/ #ifndef MITKIMAGEACCESSORBASE_H #define MITKIMAGEACCESSORBASE_H #include #include -#include -#include #include #include "mitkImageDataItem.h" +#include + namespace mitk { //##Documentation //## @brief The ImageAccessorBase class provides a lock mechanism for all inheriting image accessors. //## //## @ingroup Data class Image; /** \brief This struct allows to make ImageAccessors wait for this particular ImageAccessor object*/ struct ImageAccessorWaitLock { /** \brief Holds the number of ImageAccessors, which are waiting until the represented ImageAccessor is released. */ unsigned int m_WaiterCount; /** \brief A mutex that allows other ImageAccessors to wait for the represented ImageAccessor. */ - itk::SimpleFastMutexLock m_Mutex; + std::mutex m_Mutex; }; // Defs to assure dead lock prevention only in case of possible thread handling. #if defined(ITK_USE_SPROC) || defined(ITK_USE_PTHREADS) || defined(ITK_USE_WIN32_THREADS) #define MITK_USE_RECURSIVE_MUTEX_PREVENTION #endif class MITKCORE_EXPORT ImageAccessorBase { friend class Image; friend class ImageReadAccessor; friend class ImageWriteAccessor; template friend class ImagePixelReadAccessor; template friend class ImagePixelWriteAccessor; public: typedef itk::SmartPointer ImageConstPointer; /** \brief defines different flags for the ImageAccessor constructors */ enum Options { /** No specific Options ==> Default */ DefaultBehavior = 0, /** Defines if the Constructor waits for locked memory until it is released or not. If not, an exception is thrown.*/ ExceptionIfLocked = 1, /** Defines if requested Memory has to be coherent. If the parameter is true, it is possible that new Memory has to be allocated to arrange this desired condition. Consequently, this parameter can heavily affect computation time.*/ ForceCoherentMemory = 2, /** Ignores the lock mechanism for immediate access. Only possible with read accessors. */ IgnoreLock = 4 }; virtual ~ImageAccessorBase(); /** \brief Gives const access to the data. */ inline const void *GetData() const { return m_AddressBegin; } protected: // Define type of thread id #ifdef ITK_USE_SPROC typedef int ThreadIDType; #endif #ifdef ITK_USE_WIN32_THREADS typedef DWORD ThreadIDType; #endif #ifdef ITK_USE_PTHREADS typedef pthread_t ThreadIDType; #endif /** \brief Checks validity of given parameters from inheriting classes and stores those parameters in member * variables. */ ImageAccessorBase(ImageConstPointer iP, const ImageDataItem *iDI = nullptr, int OptionFlags = DefaultBehavior); /** ImageAccessor has access to the image it belongs to. */ // ImagePointer m_Image; /** Contains a SubRegion (always represented in maximal possible dimension) */ itk::ImageRegion<4> *m_SubRegion; /** Points to the beginning of the image part. */ void *m_AddressBegin; /** Contains the first address after the image part. */ void *m_AddressEnd; /** \brief Stores all extended properties of an ImageAccessor. * The different flags in mitk::ImageAccessorBase::Options can be unified by bitwise operations. */ int m_Options; /** Defines if the accessed image part lies coherently in memory */ bool m_CoherentMemory; /** \brief Pointer to a WaitLock struct, that allows other ImageAccessors to wait for this ImageAccessor */ ImageAccessorWaitLock *m_WaitLock; /** \brief Increments m_WaiterCount. A call of this method is prohibited unless the Mutex m_ReadWriteLock in the * mitk::Image class is Locked. */ inline void Increment() { m_WaitLock->m_WaiterCount += 1; } /** \brief Computes if there is an Overlap of the image part between this instantiation and another ImageAccessor * object * \throws mitk::Exception if memory area is incoherent (not supported yet) */ bool Overlap(const ImageAccessorBase *iAB); /** \brief Uses the WaitLock to wait for another ImageAccessor*/ void WaitForReleaseOf(ImageAccessorWaitLock *wL); ThreadIDType m_Thread; /** \brief Prevents a recursive mutex lock by comparing thread ids of competing image accessors */ void PreventRecursiveMutexLock(ImageAccessorBase *iAB); virtual const Image *GetImage() const = 0; private: /** \brief System dependend thread method, to prevent recursive mutex access */ ThreadIDType CurrentThreadHandle(); /** \brief System dependend thread method, to prevent recursive mutex access */ inline bool CompareThreadHandles(ThreadIDType, ThreadIDType); }; class MemoryIsLockedException : public Exception { public: mitkExceptionClassMacro(MemoryIsLockedException, Exception) }; } #endif diff --git a/Modules/Core/include/mitkImageGenerator.h b/Modules/Core/include/mitkImageGenerator.h index 5642b6355b..fee6d12c9d 100644 --- a/Modules/Core/include/mitkImageGenerator.h +++ b/Modules/Core/include/mitkImageGenerator.h @@ -1,236 +1,236 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 ImageGenerator_H_HEADER_INCLUDED #define ImageGenerator_H_HEADER_INCLUDED #include "mitkImageWriteAccessor.h" #include #include #include #include namespace mitk { /** * @brief generator for synthetic MITK images * This is a helper class to generate synthetic MITK images (random or gradient). * * @ingroup IO */ class MITKCORE_EXPORT ImageGenerator { public: /** * \brief Generates gradient image with the defined size and spacing */ template static mitk::Image::Pointer GenerateGradientImage(unsigned int dimX, unsigned int dimY, unsigned int dimZ, float spacingX = 1, float spacingY = 1, float spacingZ = 1) { typedef itk::Image ImageType; typename ImageType::RegionType imageRegion; imageRegion.SetSize(0, dimX); imageRegion.SetSize(1, dimY); imageRegion.SetSize(2, dimZ); typename ImageType::SpacingType spacing; spacing[0] = spacingX; spacing[1] = spacingY; spacing[2] = spacingZ; mitk::Point3D origin; origin.Fill(0.0); itk::Matrix directionMatrix; directionMatrix.SetIdentity(); typename ImageType::Pointer image = ImageType::New(); image->SetSpacing(spacing); image->SetOrigin(origin); image->SetDirection(directionMatrix); image->SetLargestPossibleRegion(imageRegion); image->SetBufferedRegion(imageRegion); image->SetRequestedRegion(imageRegion); image->Allocate(); image->FillBuffer(0.0); typedef itk::ImageRegionIterator IteratorOutputType; IteratorOutputType it(image, imageRegion); it.GoToBegin(); TPixelType val = 0; while (!it.IsAtEnd()) { it.Set(val); val++; ++it; } mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk(image.GetPointer()); mitkImage->SetVolume(image->GetBufferPointer()); return mitkImage; } /** * \brief Generates an image of a same geometry as the one given as reference The image buffer is filled to the fill_value given as parameter */ template static mitk::Image::Pointer GenerateImageFromReference(mitk::Image::Pointer reference, TPixelType fill_value) { mitk::Image::Pointer output = mitk::Image::New(); mitk::PixelType output_type = MakeScalarPixelType(); // all metadata (except type) come from reference image output->SetGeometry(reference->GetGeometry()); output->Initialize(output_type, reference->GetDimension(), reference->GetDimensions()); // get a pointer to the image buffer to write into TPixelType *imageBuffer = nullptr; try { mitk::ImageWriteAccessor writeAccess(output); imageBuffer = static_cast(writeAccess.GetData()); } catch (...) { MITK_ERROR << "Write access not granted on mitk::Image."; } // fill the buffer with the specifed value for (unsigned int i = 0; i < output->GetVolumeData(0)->GetSize(); i++) { imageBuffer[i] = fill_value; } return output; } /*! \brief Generates random image with the defined size and spacing */ template static mitk::Image::Pointer GenerateRandomImage(unsigned int dimX, unsigned int dimY, unsigned int dimZ = 1, unsigned int dimT = 1, mitk::ScalarType spacingX = 1, mitk::ScalarType spacingY = 1, mitk::ScalarType spacingZ = 1, const double randomMax = 1000.0f, const double randMin = 0.0f) { // set the data type according to the template mitk::PixelType type = MakeScalarPixelType(); // type.Initialize(typeid(TPixelType)); // initialize the MITK image with given dimenion and data type mitk::Image::Pointer output = mitk::Image::New(); auto dimensions = new unsigned int[4]; unsigned int numberOfDimensions = 0; unsigned int bufferSize = 0; // check which dimension is needed if (dimT <= 1) { if (dimZ <= 1) { // 2D numberOfDimensions = 2; dimensions[0] = dimX; dimensions[1] = dimY; bufferSize = dimX * dimY; } else { // 3D numberOfDimensions = 3; dimensions[0] = dimX; dimensions[1] = dimY; dimensions[2] = dimZ; bufferSize = dimX * dimY * dimZ; } } else { // 4D numberOfDimensions = 4; dimensions[0] = dimX; dimensions[1] = dimY; dimensions[2] = dimZ; dimensions[3] = dimT; bufferSize = dimX * dimY * dimZ * dimT; } output->Initialize(type, numberOfDimensions, dimensions); mitk::Vector3D spacing; spacing[0] = spacingX; spacing[1] = spacingY; spacing[2] = spacingZ; output->SetSpacing(spacing); // get a pointer to the image buffer to write into TPixelType *imageBuffer = nullptr; try { mitk::ImageWriteAccessor writeAccess(output); imageBuffer = static_cast(writeAccess.GetData()); } catch (...) { MITK_ERROR << "Write access not granted on mitk::Image."; } // initialize the random generator itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randomGenerator = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randomGenerator->Initialize(); // fill the buffer for each pixel/voxel for (unsigned int i = 0; i < bufferSize; i++) { // the comparison of the component type is sufficient enough since the mitk::PixelType type object is // created as SCALAR and hence does not need the comparison against type.GetPixelTypeId() == - // itk::ImageIOBase::SCALAR - if (type.GetComponentType() == itk::ImageIOBase::INT) // call integer function + // itk::IOPixelEnum::SCALAR + if (type.GetComponentType() == itk::IOComponentEnum::INT) // call integer function { imageBuffer[i] = (TPixelType)randomGenerator->GetIntegerVariate((int)randomMax); // TODO random generator does not support integer values in a given range (e.g. from 5-10) // range is always [0, (int)randomMax] } - else if ((type.GetComponentType() == itk::ImageIOBase::DOUBLE) || - (type.GetComponentType() == itk::ImageIOBase::FLOAT)) // call floating point function + else if ((type.GetComponentType() == itk::IOComponentEnum::DOUBLE) || + (type.GetComponentType() == itk::IOComponentEnum::FLOAT)) // call floating point function { imageBuffer[i] = (TPixelType)randomGenerator->GetUniformVariate(randMin, randomMax); } - else if (type.GetComponentType() == itk::ImageIOBase::UCHAR) + else if (type.GetComponentType() == itk::IOComponentEnum::UCHAR) { // use the integer randomGenerator with mod 256 to generate unsigned char values imageBuffer[i] = (unsigned char)((int)randomGenerator->GetIntegerVariate((int)randomMax)) % 256; } - else if (type.GetComponentType() == itk::ImageIOBase::USHORT) + else if (type.GetComponentType() == itk::IOComponentEnum::USHORT) { imageBuffer[i] = (unsigned short)((int)randomGenerator->GetIntegerVariate((int)randomMax)) % 65536; } else { MITK_ERROR << "Datatype not supported yet."; // TODO call different methods for other datatypes } } return output; } }; } // namespace mitk #endif /* ImageGenerator_H_HEADER_INCLUDED */ diff --git a/Modules/Core/include/mitkImageSource.h b/Modules/Core/include/mitkImageSource.h index d5a26274e8..52461c8159 100644 --- a/Modules/Core/include/mitkImageSource.h +++ b/Modules/Core/include/mitkImageSource.h @@ -1,251 +1,251 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC #define IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC #include "mitkBaseDataSource.h" #include "mitkImage.h" #include namespace mitk { /** * @brief Superclass of all classes generating Images (instances of class * Image) as output. * * In itk and vtk the generated result of a ProcessObject is only guaranteed * to be up-to-date, when Update() of the ProcessObject or the generated * DataObject is called immediately before access of the data stored in the * DataObject. This is also true for subclasses of mitk::BaseProcess and thus * for mitk::ImageSource. But there are also three access methods provided * that guarantee an up-to-date result (by first calling Update and then * returning the result of GetOutput()): GetData(), GetPic() and * GetVtkImageData(). * @ingroup Process */ class MITKCORE_EXPORT ImageSource : public BaseDataSource { public: mitkClassMacro(ImageSource, BaseDataSource); /** @brief Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** @brief Some convenient typedefs. */ typedef mitk::Image OutputImageType; typedef OutputImageType OutputType; typedef OutputImageType::Pointer OutputImagePointer; typedef SlicedData::RegionType OutputImageRegionType; /** * @brief Get the output data of this image source object. * * The output of this * function is not valid until an appropriate Update() method has * been called, either explicitly or implicitly. Both the filter * itself and the data object have Update() methods, and both * methods update the data. Here are three ways to use * GetOutput() and make sure the data is valid. In these * examples, \a image is a pointer to some Image object, and the * particular ProcessObjects involved are filters. The same * examples apply to non-image (e.g. Mesh) data as well. * * \code * anotherFilter->SetInput( someFilter->GetOutput() ); * anotherFilter->Update(); * \endcode * * In this situation, \a someFilter and \a anotherFilter are said * to constitute a \b pipeline. * * \code * image = someFilter->GetOutput(); * image->Update(); * \endcode * * \code * someFilter->Update(); * image = someFilter->GetOutput(); * \endcode * (In the above example, the two lines of code can be in * either order.) * * Note that Update() is not called automatically except within a * pipeline as in the first example. When \b streaming (using a * StreamingImageFilter) is activated, it may be more efficient to * use a pipeline than to call Update() once for each filter in * turn. * * For an image, the data generated is for the requested * Region, which can be set using ImageBase::SetRequestedRegion(). * By default, the largest possible region is requested. * * For Filters which have multiple outputs of different types, the * GetOutput() method assumes the output is of OutputImageType. For * the GetOutput(DataObjectPointerArraySizeType) method, a dynamic_cast is performed * incase the filter has outputs of different types or image * types. Derived classes should have named get methods for these * outputs. */ mitkBaseDataSourceGetOutputDeclarations /** @brief Make a DataObject of the correct type to used as the specified * output. * * Every ProcessObject subclass must be able to create a * DataObject that can be used as a specified output. This method * is automatically called when DataObject::DisconnectPipeline() is * called. DataObject::DisconnectPipeline, disconnects a data object * from being an output of its current source. When the data object * is disconnected, the ProcessObject needs to construct a replacement * output data object so that the ProcessObject is in a valid state. * So DataObject::DisconnectPipeline eventually calls * ProcessObject::MakeOutput. Note that MakeOutput always returns a * SmartPointer to a DataObject. If a subclass of ImageSource has * multiple outputs of different types, then that class must provide * an implementation of MakeOutput(). */ itk::DataObject::Pointer MakeOutput(DataObjectPointerArraySizeType idx) override; /** * This is a default implementation to make sure we have something. * Once all the subclasses of ProcessObject provide an appopriate * MakeOutput(), then ProcessObject::MakeOutput() can be made pure * virtual. */ itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override; virtual vtkImageData *GetVtkImageData(); virtual const vtkImageData *GetVtkImageData() const; protected: ImageSource(); ~ImageSource() override {} /** @brief A version of GenerateData() specific for image processing * filters. * * This implementation will split the processing across * multiple threads. The buffer is allocated by this method. Then * the BeforeThreadedGenerateData() method is called (if * provided). Then, a series of threads are spawned each calling * ThreadedGenerateData(). After all the threads have completed * processing, the AfterThreadedGenerateData() method is called (if * provided). If an image processing filter cannot be threaded, the * filter should provide an implementation of GenerateData(). That * implementation is responsible for allocating the output buffer. * If a filter an be threaded, it should NOT provide a * GenerateData() method but should provide a ThreadedGenerateData() * instead. * * \sa ThreadedGenerateData() */ void GenerateData() override; /** @brief If an imaging filter can be implemented as a multithreaded * algorithm, the filter will provide an implementation of * ThreadedGenerateData(). * * This superclass will automatically split * the output image into a number of pieces, spawn multiple threads, * and call ThreadedGenerateData() in each thread. Prior to spawning * threads, the BeforeThreadedGenerateData() method is called. After * all the threads have completed, the AfterThreadedGenerateData() * method is called. If an image processing filter cannot support * threading, that filter should provide an implementation of the * GenerateData() method instead of providing an implementation of * ThreadedGenerateData(). If a filter provides a GenerateData() * method as its implementation, then the filter is responsible for * allocating the output data. If a filter provides a * ThreadedGenerateData() method as its implementation, then the * output memory will allocated automatically by this superclass. * The ThreadedGenerateData() method should only produce the output * specified by "outputThreadRegion" * parameter. ThreadedGenerateData() cannot write to any other * portion of the output image (as this is responsibility of a * different thread). * * \sa GenerateData(), SplitRequestedRegion() */ virtual void ThreadedGenerateData(const OutputImageRegionType &outputRegionForThread, itk::ThreadIdType threadId); /** @brief This method is intentionally left blank. * * ImageSource's need not * Initialize their containers. The Image::Allocate() method (called * from GenerateData()) will resize the container if more memory is * needed. Otherwise, the memory can be reused. */ void PrepareOutputs() override; /** @brief The GenerateData method normally allocates the buffers for all of the * outputs of a filter. * * Some filters may want to override this default * behavior. For example, a filter may have multiple outputs with * varying resolution. Or a filter may want to process data in place by * grafting its input to its output.*/ virtual void AllocateOutputs(); /** @brief If an imaging filter needs to perform processing after the buffer * has been allocated but before threads are spawned, the filter can * can provide an implementation for BeforeThreadedGenerateData(). * * The execution flow in the default GenerateData() method will be: * 1) Allocate the output buffer * 2) Call BeforeThreadedGenerateData() * 3) Spawn threads, calling ThreadedGenerateData() in each thread. * 4) Call AfterThreadedGenerateData() * Note that this flow of control is only available if a filter provides * a ThreadedGenerateData() method and NOT a GenerateData() method. */ virtual void BeforeThreadedGenerateData() {} /** @brief If an imaging filter needs to perform processing after all * processing threads have completed, the filter can can provide an * implementation for AfterThreadedGenerateData(). * * The execution * flow in the default GenerateData() method will be: * 1) Allocate the output buffer * 2) Call BeforeThreadedGenerateData() * 3) Spawn threads, calling ThreadedGenerateData() in each thread. * 4) Call AfterThreadedGenerateData() * Note that this flow of control is only available if a filter provides * a ThreadedGenerateData() method and NOT a GenerateData() method. */ virtual void AfterThreadedGenerateData() {} /** @brief Split the output's RequestedRegion into "num" pieces, returning * region "i" as "splitRegion". * * This method is called "num" times. The * regions must not overlap. The method returns the number of pieces that * the routine is capable of splitting the output RequestedRegion, * i.e. return value is less than or equal to "num". */ virtual unsigned int SplitRequestedRegion(unsigned int i, unsigned int num, OutputImageRegionType &splitRegion); /** @brief Static function used as a "callback" by the MultiThreader. * * The threading library will call this routine for each thread, which will delegate the * control to ThreadedGenerateData(). */ - static ITK_THREAD_RETURN_TYPE ThreaderCallback(void *arg); + static itk::ITK_THREAD_RETURN_TYPE ThreaderCallback(void *arg); /** @brief Internal structure used for passing image data into the threading library */ struct ThreadStruct { Pointer Filter; }; private: ImageSource(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented }; } // namespace mitk #endif /* IMAGESOURCE_H_HEADER_INCLUDED_C1E7D6EC */ diff --git a/Modules/Core/include/mitkImageToItk.txx b/Modules/Core/include/mitkImageToItk.txx index dea853f238..29abe89a77 100644 --- a/Modules/Core/include/mitkImageToItk.txx +++ b/Modules/Core/include/mitkImageToItk.txx @@ -1,290 +1,290 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 IMAGETOITK_TXX_INCLUDED_C1C2FCD2 #define IMAGETOITK_TXX_INCLUDED_C1C2FCD2 #include "itkImportMitkImageContainer.h" #include "mitkBaseProcess.h" #include "mitkException.h" #include "mitkImageReadAccessor.h" #include "mitkImageToItk.h" #include "mitkImageWriteAccessor.h" #include template struct SetLengthHelper { SetLengthHelper(TImageType *in) { m_Image = in; } private: TImageType *m_Image; }; template struct SetLengthHelper> { typedef itk::Image TImageType; SetLengthHelper(TImageType *in) { m_Image = in; } void SetVectorLength(size_t) {} private: TImageType *m_Image; }; template struct SetLengthHelper> { typedef itk::VectorImage TImageType; SetLengthHelper(TImageType *in) { m_Image = in; } void SetVectorLength(size_t len) { m_Image->SetVectorLength(len); } private: TImageType *m_Image; }; template void mitk::ImageToItk::SetInput(mitk::Image *input) { this->SetInput(static_cast(input)); m_ConstInput = false; } template void mitk::ImageToItk::SetInput(const mitk::Image *input) { this->CheckInput(input); // Process object is not const-correct so the const_cast is required here itk::ProcessObject::PushFrontInput(input); m_ConstInput = true; } template mitk::Image *mitk::ImageToItk::GetInput(void) { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast(itk::ProcessObject::GetInput(0)); } template const mitk::Image *mitk::ImageToItk::GetInput() const { if (this->GetNumberOfInputs() < 1) { return nullptr; } return static_cast(itk::ProcessObject::GetInput(0)); } template void mitk::ImageToItk::GenerateData() { // Allocate output mitk::Image::Pointer input = this->GetInput(); typename Superclass::OutputImageType::Pointer output = this->GetOutput(); unsigned long noBytes = input->GetDimension(0); for (unsigned int i = 1; i < TOutputImage::GetImageDimension(); ++i) { noBytes = noBytes * input->GetDimension(i); } const mitk::PixelType pixelType = input->GetPixelType(); - if (pixelType.GetPixelType() == itk::ImageIOBase::VECTOR) + if (pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR) { noBytes *= pixelType.GetNumberOfComponents(); SetLengthHelper helper(output.GetPointer()); helper.SetVectorLength(pixelType.GetNumberOfComponents()); } std::unique_ptr imageAccess; if (m_ConstInput) { imageAccess.reset(new mitk::ImageReadAccessor(input, nullptr, m_Options)); } else { imageAccess.reset(new mitk::ImageWriteAccessor(input, nullptr, m_Options)); } // hier wird momentan wohl nur der erste Channel verwendet??!! if (imageAccess->GetData() == nullptr) { itkWarningMacro(<< "no image data to import in ITK image"); RegionType bufferedRegion; output->SetBufferedRegion(bufferedRegion); return; } if (m_CopyMemFlag) { itkDebugMacro("copyMem ..."); output->Allocate(); memcpy(output->GetBufferPointer(), imageAccess->GetData(), sizeof(InternalPixelType) * noBytes); } else { itkDebugMacro("do not copyMem ..."); typedef itk::ImportMitkImageContainer ImportContainerType; typename ImportContainerType::Pointer import; import = ImportContainerType::New(); import->Initialize(); itkDebugMacro(<< "size of container = " << import->Size()); // import->SetImageDataItem(m_ImageDataItem); import->SetImageAccessor(imageAccess.release(), sizeof(InternalPixelType) * noBytes); output->SetPixelContainer(import); itkDebugMacro(<< "size of container = " << import->Size()); } } template void mitk::ImageToItk::UpdateOutputInformation() { mitk::Image::Pointer input = this->GetInput(); if (input.IsNotNull() && (input->GetSource().IsNotNull()) && input->GetSource()->Updating()) { typename Superclass::OutputImageType::Pointer output = this->GetOutput(); unsigned long t1 = input->GetUpdateMTime() + 1; if (t1 > this->m_OutputInformationMTime.GetMTime()) { output->SetPipelineMTime(t1); this->GenerateOutputInformation(); this->m_OutputInformationMTime.Modified(); } return; } Superclass::UpdateOutputInformation(); } template void mitk::ImageToItk::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); typename Superclass::OutputImageType::Pointer output = this->GetOutput(); // allocate size, origin, spacing, direction in types of output image SizeType size; const unsigned int itkDimMin3 = (TOutputImage::ImageDimension > 3 ? TOutputImage::ImageDimension : 3); const unsigned int itkDimMax3 = (TOutputImage::ImageDimension < 3 ? TOutputImage::ImageDimension : 3); typename Superclass::OutputImageType::PointType::ValueType origin[itkDimMin3]; typename Superclass::OutputImageType::SpacingType::ComponentType spacing[itkDimMin3]; typename Superclass::OutputImageType::DirectionType direction; // copy as much information as possible into size and spacing unsigned int i; for (i = 0; i < itkDimMax3; ++i) { size[i] = input->GetDimension(i); spacing[i] = input->GetGeometry()->GetSpacing()[i]; } for (; i < TOutputImage::ImageDimension; ++i) { origin[i] = 0.0; size[i] = input->GetDimension(i); spacing[i] = 1.0; } // build region from size IndexType start; start.Fill(0); RegionType region; region.SetIndex(start); region.SetSize(size); // copy as much information as possible into origin const mitk::Point3D &mitkorigin = input->GetGeometry()->GetOrigin(); itk2vtk(mitkorigin, origin); // copy as much information as possible into direction direction.SetIdentity(); unsigned int j; const AffineTransform3D::MatrixType &matrix = input->GetGeometry()->GetIndexToWorldTransform()->GetMatrix(); /// \warning 2D MITK images could have a 3D rotation, since they have a 3x3 geometry matrix. /// If it is only a rotation around the axial plane normal, it can be express with a 2x2 matrix. /// In this case, the ITK image conservs this information and is identical to the MITK image! /// If the MITK image contains any other rotation, the ITK image will have no rotation at all. /// Spacing is of course conserved in both cases. // the following loop devides by spacing now to normalize columns. // counterpart of InitializeByItk in mitkImage.h line 372 of revision 15092. // Check if information is lost if (TOutputImage::ImageDimension <= 2) { if ((TOutputImage::ImageDimension == 2) && ((matrix[0][2] != 0) || (matrix[1][2] != 0) || (matrix[2][0] != 0) || (matrix[2][1] != 0) || ((matrix[2][2] != 1) && (matrix[2][2] != -1)))) { // The 2D MITK image contains 3D rotation information. // This cannot be expressed in a 2D ITK image, so the ITK image will have no rotation } else { // The 2D MITK image can be converted to an 2D ITK image without information loss! for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) direction[i][j] = matrix[i][j] / spacing[j]; } } else { // Normal 3D image. Conversion possible without problem! for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) direction[i][j] = matrix[i][j] / spacing[j]; } // set information into output image output->SetRegions(region); output->SetOrigin(origin); output->SetSpacing(spacing); output->SetDirection(direction); } template void mitk::ImageToItk::CheckInput(const mitk::Image *input) const { if (input == nullptr) { itkExceptionMacro(<< "image is null"); } if (input->GetDimension() != TOutputImage::GetImageDimension()) { itkExceptionMacro(<< "image has dimension " << input->GetDimension() << " instead of " << TOutputImage::GetImageDimension()); } if (!(input->GetPixelType() == mitk::MakePixelType(input->GetPixelType().GetNumberOfComponents()))) { itkExceptionMacro(<< "image has wrong pixel type "); } } template void mitk::ImageToItk::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); } #endif // IMAGETOITK_TXX_INCLUDED_C1C2FCD2 diff --git a/Modules/Core/include/mitkInteractionSchemeSwitcher.h b/Modules/Core/include/mitkInteractionSchemeSwitcher.h index 139d6f0738..bd4aa4105c 100644 --- a/Modules/Core/include/mitkInteractionSchemeSwitcher.h +++ b/Modules/Core/include/mitkInteractionSchemeSwitcher.h @@ -1,116 +1,116 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKINTERACTIONSCHEMESWITCHER_H #define MITKINTERACTIONSCHEMESWITCHER_H #include "MitkCoreExports.h" #include "mitkInteractionEventHandler.h" #include namespace mitk { +#pragma GCC visibility push(default) + /** + \brief Can be observed by GUI class to update button states when type is changed programmatically. + */ + itkEventMacroDeclaration(InteractionSchemeChangedEvent, itk::AnyEvent); +#pragma GCC visibility pop + /*********************************************************************** * * \brief Class that offers a convenient way to switch between different * interaction schemes. * * This class offers the possibility to switch between the different * interaction schemes that are available: * * - MITKStandard : The original MITK interaction scheme * - MITKRotationUncoupled : A modified MITK interaction scheme with rotation * - MITKRotationCoupled : A modified MTIK interaction scheme with coupled rotation * - MITKSwivel : A modified MITK interaction scheme with plane swiveling * * - PACS : An alternative interaction scheme that behaves more like a * PACS workstation * - left mouse button : behavior depends on current PACS scheme * Always enabled: * - middle mouse button : fast scrolling * - right mouse button : level-window * - ctrl + right button : zooming * - shift+ right button : panning * * There are 6 different PACS schemes. * Each scheme defines the interaction that is performed on a left * mouse button click: * - PACSBase : No interaction on a left mouse button click - This scheme serves as a base for other PACS schemes and defines the right and middle mouse button clicks, which are available in every PACS scheme. * - PACSStandard : Sets the cross position for the MPR * - PACSLevelWindow : Sets the level window * - PACSPan : Moves the slice * - PACSScroll : Scrolls through the slices stepwise * - PACSZoom : Zooms into / out of the slice * * When the interaction scheme is changed, this class sets the corresponding * interaction .xml-files for a given interaction event handler. * ***********************************************************************/ class MITKCORE_EXPORT InteractionSchemeSwitcher : public itk::Object { public: -#pragma GCC visibility push(default) - /** - \brief Can be observed by GUI class to update button states when type is changed programmatically. - */ - itkEventMacro(InteractionSchemeChangedEvent, itk::AnyEvent); -#pragma GCC visibility pop - mitkClassMacroItkParent(InteractionSchemeSwitcher, itk::Object); itkFactorylessNewMacro(Self); // enum of the different interaction schemes that are available enum InteractionScheme { MITKStandard = 0, MITKRotationUncoupled, MITKRotationCoupled, MITKSwivel, PACSBase, PACSStandard, PACSLevelWindow, PACSPan, PACSScroll, PACSZoom }; /** * @brief Set the current interaction scheme of the given interaction event handler * * The interaction event handler is able to accept xml-configuration files that will define the interaction scheme. * Based on the given interaction scheme different configuration files are loaded into the interaction event handler. * The interaction scheme can be a variant of the MITK-scheme or the PACS-scheme (see 'enum InteractionScheme'). * The default is 'MITKStandard'. * If the interaction scheme has been changed, an 'InteractionSchemeChangedEvent' will be invoked. * * @pre The interaction event handler has to be valid (!nullptr). * @throw mitk::Exception, if the interaction event handler is invalid (==nullptr). * * @param interactionEventHandler The interaction event handler that defines the interaction scheme via configuration files * @param interactionScheme The interaction scheme that should be used for the currently active interaction event handler. */ void SetInteractionScheme(mitk::InteractionEventHandler* interactionEventHandler, InteractionScheme interactionScheme); protected: InteractionSchemeSwitcher(); ~InteractionSchemeSwitcher() override; }; } // namespace mitk #endif // MITKINTERACTIONSCHEMESWITCHER_H diff --git a/Modules/Core/include/mitkLimitedLinearUndo.h b/Modules/Core/include/mitkLimitedLinearUndo.h index fee98b5b91..3d724b1db7 100644 --- a/Modules/Core/include/mitkLimitedLinearUndo.h +++ b/Modules/Core/include/mitkLimitedLinearUndo.h @@ -1,159 +1,159 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef LIMITEDLINEARUNDO_H_HEADER_INCLUDED_C16E9C96 #define LIMITEDLINEARUNDO_H_HEADER_INCLUDED_C16E9C96 // MITK header #include "mitkOperationEvent.h" #include "mitkUndoModel.h" #include // STL header #include // ITK header #pragma GCC visibility push(default) #include #pragma GCC visibility pop #include namespace mitk { //##Documentation //## @brief A linear undo model with one undo and one redo stack. //## //## Derived from UndoModel AND itk::Object. Invokes ITK-events to signal listening //## GUI elements, whether each of the stacks is empty or not (to enable/disable button, ...) class MITKCORE_EXPORT LimitedLinearUndo : public UndoModel { public: typedef std::deque UndoContainer; typedef std::deque::reverse_iterator UndoContainerRevIter; mitkClassMacro(LimitedLinearUndo, UndoModel); itkFactorylessNewMacro(Self); itkCloneMacro(Self); bool SetOperationEvent(UndoStackItem *stackItem) override; //##Documentation //## @brief Undoes the last changes //## //## Reads the top element of the Undo-Stack, //## executes the operation, //## swaps the OperationEvent-Undo with the Operation //## and sets it to Redo-Stack bool Undo() override; bool Undo(bool) override; //##Documentation //## @brief Undoes all changes until ObjectEventID oeid virtual bool Undo(int oeid); //##Documentation //## @brief Undoes the last changes //## //## Reads the top element of the Redo-Stack, //## executes the operation, //## swaps the OperationEvent-Operation with the Undo-Operation //## and sets it to Undo-Stack bool Redo() override; bool Redo(bool) override; //##Documentation //## @brief Redoes all changes until ObjectEventID oeid virtual bool Redo(int oeid); //##Documentation //## @brief Clears UndoList and RedoList void Clear() override; //##Documentation //## @brief Clears the RedoList void ClearRedoList() override; //##Documentation //## @brief True, if RedoList is empty bool RedoListEmpty() override; //##Documentation //## @brief Gets the limit on the size of the undo history. //## The undo limit determines how many items can be stored //## in the undo stack. If the value is 0 that means that //## there is no limit. std::size_t GetUndoLimit() const override; //##Documentation //## @brief Sets a limit on the size of the undo history. //## If the limit is reached, the oldest undo items will //## be dropped from the bottom of the undo stack. //## The 0 value means that there is no limit. //## @param limit the maximum number of items on the stack void SetUndoLimit(std::size_t limit) override; //##Documentation //## @brief Returns the ObjectEventId of the //## top element in the OperationHistory int GetLastObjectEventIdInList() override; //##Documentation //## @brief Returns the GroupEventId of the //## top element in the OperationHistory int GetLastGroupEventIdInList() override; //##Documentation //## @brief Returns the last specified OperationEvent in Undo-list //## corresponding to the given values; if nothing found, then returns nullptr OperationEvent *GetLastOfType(OperationActor *destination, OperationType opType) override; protected: //##Documentation //## Constructor LimitedLinearUndo(); //##Documentation //## Destructor ~LimitedLinearUndo() override; //## @brief Convenience method to free the memory of //## elements in the list and to clear the list void ClearList(UndoContainer *list); UndoContainer m_UndoList; UndoContainer m_RedoList; private: int FirstObjectEventIdOfCurrentGroup(UndoContainer &stack); std::size_t m_UndoLimit; }; #pragma GCC visibility push(default) /// Some itk events to notify listening GUI elements, when the undo or redo stack is empty (diable undo button) /// or when there are items in the stack (enable button) - itkEventMacro(UndoStackEvent, itk::ModifiedEvent); - itkEventMacro(UndoEmptyEvent, UndoStackEvent); - itkEventMacro(RedoEmptyEvent, UndoStackEvent); - itkEventMacro(UndoNotEmptyEvent, UndoStackEvent); - itkEventMacro(RedoNotEmptyEvent, UndoStackEvent); + itkEventMacroDeclaration(UndoStackEvent, itk::ModifiedEvent); + itkEventMacroDeclaration(UndoEmptyEvent, UndoStackEvent); + itkEventMacroDeclaration(RedoEmptyEvent, UndoStackEvent); + itkEventMacroDeclaration(UndoNotEmptyEvent, UndoStackEvent); + itkEventMacroDeclaration(RedoNotEmptyEvent, UndoStackEvent); /// Additional unused events, if anybody wants to put an artificial limit to the possible number of items in the stack - itkEventMacro(UndoFullEvent, UndoStackEvent); - itkEventMacro(RedoFullEvent, UndoStackEvent); + itkEventMacroDeclaration(UndoFullEvent, UndoStackEvent); + itkEventMacroDeclaration(RedoFullEvent, UndoStackEvent); #pragma GCC visibility pop } // namespace mitk #endif /* LIMITEDLINEARUNDO_H_HEADER_INCLUDED_C16E9C96 */ diff --git a/Modules/Core/include/mitkMessage.h b/Modules/Core/include/mitkMessage.h index 47cf00cac8..564472316c 100644 --- a/Modules/Core/include/mitkMessage.h +++ b/Modules/Core/include/mitkMessage.h @@ -1,697 +1,697 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitkMessageHIncluded #define mitkMessageHIncluded #include -#include +#include #include /** * Adds a Message<> variable and methods to add/remove message delegates to/from * this variable. */ #define mitkNewMessageMacro(msgHandleObject) \ private: \ ::mitk::Message<> m_##msgHandleObject##Message; \ \ public: \ inline void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate<> &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ inline void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate<> &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } #define mitkNewMessageWithReturnMacro(msgHandleObject, returnType) \ private: \ ::mitk::Message m_##msgHandleObject##Message; \ \ public: \ inline void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ inline void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } #define mitkNewMessage1Macro(msgHandleObject, type1) \ private: \ ::mitk::Message1 m_##msgHandleObject##Message; \ \ public: \ void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate1 &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate1 &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } #define mitkNewMessage2Macro(msgHandleObject, type1, type2) \ private: \ ::mitk::Message2 m_##msgHandleObject##Message; \ \ public: \ void Add##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate2 &delegate) \ { \ m_##msgHandleObject##Message += delegate; \ } \ void Remove##msgHandleObject##Listener(const ::mitk::MessageAbstractDelegate2 &delegate) \ { \ m_##msgHandleObject##Message -= delegate; \ } namespace mitk { template class MessageAbstractDelegate { public: virtual ~MessageAbstractDelegate() {} virtual A Execute() const = 0; virtual bool operator==(const MessageAbstractDelegate *cmd) const = 0; virtual MessageAbstractDelegate *Clone() const = 0; }; template class MessageAbstractDelegate1 { public: virtual ~MessageAbstractDelegate1() {} virtual A Execute(T t) const = 0; virtual bool operator==(const MessageAbstractDelegate1 *cmd) const = 0; virtual MessageAbstractDelegate1 *Clone() const = 0; }; template class MessageAbstractDelegate2 { public: virtual ~MessageAbstractDelegate2() {} virtual A Execute(T t, U u) const = 0; virtual bool operator==(const MessageAbstractDelegate2 *cmd) const = 0; virtual MessageAbstractDelegate2 *Clone() const = 0; }; template class MessageAbstractDelegate3 { public: virtual ~MessageAbstractDelegate3() {} virtual A Execute(T t, U u, V v) const = 0; virtual bool operator==(const MessageAbstractDelegate3 *cmd) const = 0; virtual MessageAbstractDelegate3 *Clone() const = 0; }; template class MessageAbstractDelegate4 { public: virtual ~MessageAbstractDelegate4() {} virtual A Execute(T t, U u, V v, W w) const = 0; virtual bool operator==(const MessageAbstractDelegate4 *cmd) const = 0; virtual MessageAbstractDelegate4 *Clone() const = 0; }; /** * This class essentially wraps a function pointer with signature * A(R::*function)(). A is the return type of your callback function * and R the type of the class implementing the function. * * Use this class to add a callback function to * messages without parameters. */ template class MessageDelegate : public MessageAbstractDelegate { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate(R *object, A (R::*memberFunctionPointer)()) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate() override {} // override function "Call" A Execute() const override { return (m_Object->*m_MemberFunctionPointer)(); // execute member function } bool operator==(const MessageAbstractDelegate *c) const override { const MessageDelegate *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate *Clone() const override { return new MessageDelegate(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(); // pointer to member function }; /** * This class essentially wraps a function pointer with signature * A(R::*function)(T). A is the return type of your callback function, * R the type of the class implementing the function and T the type * of the argument. * * Use this class to add a callback function to * messages with one parameter. * * If you need more parameters, use MessageDelegate2 etc. */ template class MessageDelegate1 : public MessageAbstractDelegate1 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate1(R *object, A (R::*memberFunctionPointer)(T)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate1() override {} // override function "Call" A Execute(T t) const override { return (m_Object->*m_MemberFunctionPointer)(t); // execute member function } bool operator==(const MessageAbstractDelegate1 *c) const override { const MessageDelegate1 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate1 *Clone() const override { return new MessageDelegate1(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T); // pointer to member function }; template class MessageDelegate2 : public MessageAbstractDelegate2 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate2(R *object, A (R::*memberFunctionPointer)(T, U)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate2() override {} // override function "Call" A Execute(T t, U u) const override { return (m_Object->*m_MemberFunctionPointer)(t, u); // execute member function } bool operator==(const MessageAbstractDelegate2 *c) const override { const MessageDelegate2 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate2 *Clone() const override { return new MessageDelegate2(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T, U); // pointer to member function }; template class MessageDelegate3 : public MessageAbstractDelegate3 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate3(R *object, A (R::*memberFunctionPointer)(T, U, V)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } ~MessageDelegate3() override {} // override function "Call" A Execute(T t, U u, V v) const override { return (m_Object->*m_MemberFunctionPointer)(t, u, v); // execute member function } bool operator==(const MessageAbstractDelegate3 *c) const override { const MessageDelegate3 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate3 *Clone() const override { return new MessageDelegate3(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T, U, V); // pointer to member function }; template class MessageDelegate4 : public MessageAbstractDelegate4 { public: // constructor - takes pointer to an object and pointer to a member and stores // them in two private variables MessageDelegate4(R *object, A (R::*memberFunctionPointer)(T, U, V, W)) : m_Object(object), m_MemberFunctionPointer(memberFunctionPointer) { } virtual ~MessageDelegate4() {} // override function "Call" virtual A Execute(T t, U u, V v, W w) const { return (m_Object->*m_MemberFunctionPointer)(t, u, v, w); // execute member function } bool operator==(const MessageAbstractDelegate4 *c) const { const MessageDelegate4 *cmd = dynamic_cast *>(c); if (!cmd) return false; if ((void *)this->m_Object != (void *)cmd->m_Object) return false; if (this->m_MemberFunctionPointer != cmd->m_MemberFunctionPointer) return false; return true; } MessageAbstractDelegate4 *Clone() const { return new MessageDelegate4(m_Object, m_MemberFunctionPointer); } private: R *m_Object; // pointer to object A (R::*m_MemberFunctionPointer)(T, U, V, W); // pointer to member function }; template class MessageBase { public: typedef std::vector ListenerList; virtual ~MessageBase() { for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter) { delete *iter; } } MessageBase() {} MessageBase(const MessageBase &o) { for (typename ListenerList::iterator iter = o.m_Listeners.begin(); iter != o.m_Listeners.end(); ++iter) { m_Listeners.push_back((*iter)->Clone()); } } MessageBase &operator=(const MessageBase &o) { MessageBase tmp(o); std::swap(tmp.m_Listeners, this->m_Listeners); return *this; } void AddListener(const AbstractDelegate &delegate) const { AbstractDelegate *msgCmd = delegate.Clone(); - m_Mutex.Lock(); + m_Mutex.lock(); for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter) { if ((*iter)->operator==(msgCmd)) { delete msgCmd; - m_Mutex.Unlock(); + m_Mutex.unlock(); return; } } m_Listeners.push_back(msgCmd); - m_Mutex.Unlock(); + m_Mutex.unlock(); } void operator+=(const AbstractDelegate &delegate) const { this->AddListener(delegate); } void RemoveListener(const AbstractDelegate &delegate) const { - m_Mutex.Lock(); + m_Mutex.lock(); for (auto iter = m_Listeners.begin(); iter != m_Listeners.end(); ++iter) { if ((*iter)->operator==(&delegate)) { delete *iter; m_Listeners.erase(iter); - m_Mutex.Unlock(); + m_Mutex.unlock(); return; } } - m_Mutex.Unlock(); + m_Mutex.unlock(); } void operator-=(const AbstractDelegate &delegate) const { this->RemoveListener(delegate); } const ListenerList &GetListeners() const { return m_Listeners; } bool HasListeners() const { return !m_Listeners.empty(); } bool IsEmpty() const { return m_Listeners.empty(); } protected: /** * \brief List of listeners. * * This is declared mutable for a reason: Imagine an object that sends out notifications and * someone gets a const Database object, because he/she should not write to the * database. He/she should anyway be able to register for notifications about changes in the database * -- this is why AddListener and RemoveListener are declared const. m_Listeners must be * mutable so that AddListener and RemoveListener can modify it regardless of the object's constness. */ mutable ListenerList m_Listeners; - mutable itk::SimpleFastMutexLock m_Mutex; + mutable std::mutex m_Mutex; }; /** * \brief Event/message/notification class. * * \sa mitk::BinaryThresholdTool * \sa QmitkBinaryThresholdToolGUI * * This totally ITK, Qt, VTK, whatever toolkit independent class * allows one class to send out messages and another class to * receive these message. This class is templated over the * return type (A) of the callback functions. * There are variations of this class * (Message1, Message2, etc.) for sending * one, two or more parameters along with the messages. * * This is an implementation of the Observer pattern. * * \li There is no guarantee about the order of which observer is notified first. At the moment the observers which * register first will be notified first. * \li Notifications are synchronous, by direct method calls. There is no support for asynchronous messages. * * To conveniently add methods for registering/unregistering observers * to Message variables of your class, you can use the mitkNewMessageMacro * macros. */ template class Message : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send() const { ListenerList listeners; { - this->m_Mutex.Lock(); + this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); - this->m_Mutex.Unlock(); + this->m_Mutex.unlock(); } for (auto iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(); } } void operator()() const { this->Send(); } }; // message with 1 parameter and return type template class Message1 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t) const { ListenerList listeners; { - this->m_Mutex.Lock(); + this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); - this->m_Mutex.Unlock(); + this->m_Mutex.unlock(); } for (auto iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t); } } void operator() (T t) const { this->Send(t); } }; // message with 2 parameters and return type template class Message2 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t, U u) const { ListenerList listeners; { - this->m_Mutex.Lock(); + this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); - this->m_Mutex.Unlock(); + this->m_Mutex.unlock(); } for (auto iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t, u); } } void operator()(T t, U u) const { this->Send(t, u); } }; // message with 3 parameters and return type template class Message3 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t, U u, V v) const { ListenerList listeners; { - this->m_Mutex.Lock(); + this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); - this->m_Mutex.Unlock(); + this->m_Mutex.unlock(); } for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t, u, v); } } void operator()(T t, U u, V v) const { this->Send(t, u, v); } }; // message with 4 parameters and return type template class Message4 : public MessageBase> { public: typedef MessageBase> Super; typedef typename Super::ListenerList ListenerList; void Send(T t, U u, V v, W w) const { ListenerList listeners; { - this->m_Mutex.Lock(); + this->m_Mutex.lock(); listeners.assign(this->m_Listeners.begin(), this->m_Listeners.end()); - this->m_Mutex.Unlock(); + this->m_Mutex.unlock(); } for (typename ListenerList::iterator iter = listeners.begin(); iter != listeners.end(); ++iter) { // notify each listener (*iter)->Execute(t, u, v, w); } } void operator()(T t, U u, V v, W w) const { this->Send(t, u, v, w); } }; /* Here is an example how to use the macros and templates: * * // An object to be send around * class Law * { * private: * std::string m_Description; * * public: * * Law(const std::string law) : m_Description(law) * { } * * std::string GetDescription() const * { * return m_Description; * } * }; * * // The NewtonMachine will issue specific events * class NewtonMachine * { * mitkNewMessageMacro(AnalysisStarted); * mitkNewMessage1Macro(AnalysisStopped, bool); * mitkNewMessage1Macro(LawDiscovered, const Law&); * * public: * * void StartAnalysis() * { * // send the "started" signal * m_AnalysisStartedMessage(); * * // we found a new law of nature by creating one :-) * Law massLaw("F=ma"); * m_LawDiscoveredMessage(massLaw); * } * * void StopAnalysis() * { * // send the "stop" message with false, indicating * // that no error occured * m_AnalysisStoppedMessage(false); * } * }; * * class Observer * { * private: * * NewtonMachine* m_Machine; * * public: * * Observer(NewtonMachine* machine) : m_Machine(machine) * { * // Add "observers", i.e. function pointers to the machine * m_Machine->AddAnalysisStartedListener( * ::mitk::MessageDelegate(this, &Observer::MachineStarted)); * m_Machine->AddAnalysisStoppedListener( * ::mitk::MessageDelegate1(this, &Observer::MachineStopped)); * m_Machine->AddLawDiscoveredListener( * ::mitk::MessageDelegate1(this, &Observer::LawDiscovered)); * } * * ~Observer() * { * // Always remove your observers when finished * m_Machine->RemoveAnalysisStartedListener( * ::mitk::MessagDelegate(this, &Observer::MachineStarted)); * m_Machine->RemoveAnalysisStoppedListener( * ::mitk::MessageDelegate1(this, &Observer::MachineStopped)); * m_Machine->RemoveLawDiscoveredListener( * ::mitk::MessageDelegate1(this, &Observer::LawDiscovered)); * } * * void MachineStarted() * { * std::cout << "Observed machine has started" << std::endl; * } * * void MachineStopped(bool error) * { * std::cout << "Observed machine stopped " << (error ? "with an error" : "") << std::endl; * } * * void LawDiscovered(const Law& law) * { * std::cout << "New law of nature discovered: " << law.GetDescription() << std::endl; * } * }; * * NewtonMachine newtonMachine; * Observer observer(&newtonMachine); * * // This will send two events to registered observers * newtonMachine.StartAnalysis(); * // This will send one event to registered observers * newtonMachine.StopAnalysis(); * * Another example of how to use these message classes can be * found in the directory Testing, file mitkMessageTest.cpp * */ } // namespace #endif diff --git a/Modules/Core/include/mitkPixelType.h b/Modules/Core/include/mitkPixelType.h index 3464c89f37..8df6a5e3b8 100644 --- a/Modules/Core/include/mitkPixelType.h +++ b/Modules/Core/include/mitkPixelType.h @@ -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. ============================================================================*/ #ifndef mitkPixelType_h #define mitkPixelType_h #include "mitkCommon.h" #include "mitkPixelTypeTraits.h" #include #include #include #include #include #include namespace mitk { template std::string PixelComponentTypeToString() { return itk::ImageIOBase::GetComponentTypeAsString(itk::ImageIOBase::MapPixelType::CType); } template std::string PixelTypeToString() { return std::string(); } /** * @brief Class for defining the data type of pixels * * To obtain additional type information not provided by this class * itk::ImageIOBase can be used by passing the return value of * PixelType::GetItkTypeId() to itk::ImageIOBase::SetPixelTypeInfo * and using the itk::ImageIOBase methods GetComponentType, * GetComponentTypeAsString, GetPixelType, GetPixelTypeAsString. * @ingroup Data */ class MITKCORE_EXPORT PixelType { public: - typedef itk::ImageIOBase::IOPixelType ItkIOPixelType; - typedef itk::ImageIOBase::IOComponentType ItkIOComponentType; + typedef itk::IOPixelEnum ItkIOPixelType; + typedef itk::IOComponentEnum ItkIOComponentType; PixelType(const mitk::PixelType &aPixelType); PixelType &operator=(const PixelType &other); - itk::ImageIOBase::IOPixelType GetPixelType() const; + ItkIOPixelType GetPixelType() const; /** * \brief Get the \a component type (the scalar (!) type). Each element * may contain m_NumberOfComponents (more than one) of these scalars. * */ - int GetComponentType() const; + ItkIOComponentType GetComponentType() const; /** * \brief Returns a string containing the ITK pixel type name. */ std::string GetPixelTypeAsString() const; /** * \brief Returns a string containing the name of the component. */ std::string GetComponentTypeAsString() const; /** * \brief Returns a string representing the pixel type and pixel components. */ std::string GetTypeAsString() const; /** * \brief Get size of the PixelType in bytes * * A RGBA PixelType of floats will return 4 * sizeof(float) */ size_t GetSize() const; /** * \brief Get the number of bits per element (of an * element) * * A vector of double with three components will return * 8*sizeof(double)*3. * \sa GetBitsPerComponent * \sa GetItkTypeId * \sa GetTypeId */ size_t GetBpe() const; /** * \brief Get the number of components of which each element consists * * Each pixel can consist of multiple components, e.g. RGB. */ size_t GetNumberOfComponents() const; /** * \brief Get the number of bits per components * \sa GetBitsPerComponent */ size_t GetBitsPerComponent() const; bool operator==(const PixelType &rhs) const; bool operator!=(const PixelType &rhs) const; ~PixelType(); private: friend PixelType MakePixelType(const itk::ImageIOBase *imageIO); template friend PixelType MakePixelType(std::size_t numOfComponents); template friend PixelType MakePixelType(); template friend PixelType MakePixelType(size_t); - PixelType(const int componentType, - const ItkIOPixelType pixelType, + PixelType(ItkIOComponentType componentType, + ItkIOPixelType pixelType, std::size_t bytesPerComponent, std::size_t numOfComponents, const std::string &componentTypeName, const std::string &pixelTypeName); // default constructor is disabled on purpose PixelType(void); /** \brief the \a type_info of the scalar (!) component type. Each element may contain m_NumberOfComponents (more than one) of these scalars. */ - int m_ComponentType; + ItkIOComponentType m_ComponentType; ItkIOPixelType m_PixelType; std::string m_ComponentTypeName; std::string m_PixelTypeName; std::size_t m_NumberOfComponents; std::size_t m_BytesPerComponent; }; /** * @brief deduct the PixelType for a given vtk image * * @param vtkimagedata the image the PixelType shall be deducted from * @return the mitk::PixelType */ MITKCORE_EXPORT mitk::PixelType MakePixelType(vtkImageData *vtkimagedata); /** * \brief A template method for creating a pixel type. */ template PixelType MakePixelType(std::size_t numOfComponents) { return PixelType(MapPixelType::value>::IOComponentType, MapPixelType::value>::IOPixelType, sizeof(ComponentT), numOfComponents, PixelComponentTypeToString(), PixelTypeToString()); } /** * \brief A template method for creating a pixel type. * * @deprecated, use version with one numOfComponents as function argument instead. */ template PixelType MakePixelType() { return MakePixelType(numOfComponents); } /** * \brief A helper template for compile-time checking of supported ITK image types. * * Unsupported image types will be marked by template specializations with * missing definitions; */ template struct AssertImageTypeIsValid { }; // The itk::VariableLengthVector pixel type is not supported in MITK if it is // used with an itk::Image (it cannot be represented as a mitk::Image object). // Use a itk::VectorImage instead. template struct AssertImageTypeIsValid, VImageDimension>>; /** \brief A template method for creating a MITK pixel type na ITK image type * * \param numOfComponents The number of components for the pixel type of the ITK image */ template PixelType MakePixelType(std::size_t numOfComponents) { AssertImageTypeIsValid(); // define new type, since the ::PixelType is used to distinguish between simple and compound types typedef typename ItkImageType::PixelType ImportPixelType; // get the component type ( is either directly ImportPixelType or ImportPixelType::ValueType for compound types ) typedef typename GetComponentType::ComponentType ComponentT; // The PixelType is the same as the ComponentT for simple types typedef typename ItkImageType::PixelType PixelT; // call the constructor return PixelType(MapPixelType::value>::IOComponentType, MapPixelType::value>::IOPixelType, sizeof(ComponentT), numOfComponents, PixelComponentTypeToString(), PixelTypeToString()); } /** \brief A template method for creating a MITK pixel type from an ITK image * pixel type and dimension * * \param numOfComponents The number of components for the pixel type \c TPixelType */ template PixelType MakePixelType(std::size_t numOfComponents) { typedef typename ImageTypeTrait::ImageType ItkImageType; return MakePixelType(numOfComponents); } /** \brief A template method for creating a MITK pixel type from an ITK image * pixel type and dimension * * For images where the number of components of the pixel type is determined at * runtime (e.g. pixel types like itk::VariableLengthVector) the * MakePixelType(std::size_t) function must be used. */ template PixelType MakePixelType() { if (ImageTypeTrait::IsVectorImage) { mitkThrow() << " Variable pixel type given but the length is not specified. Use the parametric MakePixelType( " "size_t ) method instead."; } // Use the InternalPixelType to get "1" for the number of components in case of // a itk::VectorImage typedef typename ItkImageType::InternalPixelType PixelT; const std::size_t numComp = ComponentsTrait::value, ItkImageType>::Size; // call the constructor return MakePixelType(numComp); } /** * \brief Create a MITK pixel type based on a itk::ImageIOBase object */ inline PixelType MakePixelType(const itk::ImageIOBase *imageIO) { return mitk::PixelType(imageIO->GetComponentType(), imageIO->GetPixelType(), imageIO->GetComponentSize(), imageIO->GetNumberOfComponents(), imageIO->GetComponentTypeAsString(imageIO->GetComponentType()), imageIO->GetPixelTypeAsString(imageIO->GetPixelType())); } /** \brief An interface to the MakePixelType method for creating scalar pixel types. * * Usage: for example MakeScalarPixelType() for a scalar short image */ template PixelType MakeScalarPixelType() { return MakePixelType(); } } // namespace mitk #endif /* mitkPixelType_h */ diff --git a/Modules/Core/include/mitkPixelTypeMultiplex.h b/Modules/Core/include/mitkPixelTypeMultiplex.h index ed235bd36b..94efdb34de 100644 --- a/Modules/Core/include/mitkPixelTypeMultiplex.h +++ b/Modules/Core/include/mitkPixelTypeMultiplex.h @@ -1,173 +1,173 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKPIXELTYPEMULTIPLEX_H #define MITKPIXELTYPEMULTIPLEX_H #define mitkPixelTypeMultiplex0(function, ptype) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype); \ } #define mitkPixelTypeMultiplex1(function, ptype, param1) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype, param1); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype, param1); \ } #define mitkPixelTypeMultiplex2(function, ptype, param1, param2) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype, param1, param2); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype, param1, param2); \ } #define mitkPixelTypeMultiplex3(function, ptype, param1, param2, param3) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype, param1, param2, param3); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype, param1, param2, param3); \ } // we have to have a default for, else Clang 3.6.1 complains about problems if 'if evaluates to false' // therefore if type does not match double is assumed #define mitkPixelTypeMultiplex4(function, ptype, param1, param2, param3, param4) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype, param1, param2, param3, param4); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype, param1, param2, param3, param4); \ else if (true) \ function(ptype, param1, param2, param3, param4); \ } // we have to have a default for, else Clang 3.6.1 complains about problems if 'if evaluates to false' // therefore if type does not match double is assumed #define mitkPixelTypeMultiplex5(function, ptype, param1, param2, param3, param4, param5) \ \ { \ - if (ptype.GetComponentType() == itk::ImageIOBase::CHAR) \ + if (ptype.GetComponentType() == itk::IOComponentEnum::CHAR) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UCHAR) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UCHAR) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::SHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::SHORT) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::USHORT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::USHORT) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::INT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::INT) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::UINT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::UINT) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::LONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::LONG) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::ULONG) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::ULONG) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::FLOAT) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::FLOAT) \ function(ptype, param1, param2, param3, param4, param5); \ - else if (ptype.GetComponentType() == itk::ImageIOBase::DOUBLE) \ + else if (ptype.GetComponentType() == itk::IOComponentEnum::DOUBLE) \ function(ptype, param1, param2, param3, param4, param5); \ else \ function(ptype, param1, param2, param3, param4, param5); \ } #endif // MITKPIXELTYPEMULTIPLEX_H diff --git a/Modules/Core/include/mitkPixelTypeTraits.h b/Modules/Core/include/mitkPixelTypeTraits.h index 16772df28d..a5f190d33a 100644 --- a/Modules/Core/include/mitkPixelTypeTraits.h +++ b/Modules/Core/include/mitkPixelTypeTraits.h @@ -1,271 +1,272 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 PIXELTYPETRAITS_H #define PIXELTYPETRAITS_H #include #include #include #include #include #include /** \file mitkPixelTypeTraits.h * * The pixel type traits are in general used for compile time resolution of the component type and * the number of components for compound types like the ones in ItkImageType. * The default values are used to define the corresponding variable also for scalar types */ namespace itk { /** Forward declaration of the Variable Length Vector class from ITK */ template class VariableLengthVector; } #define MITK_PIXEL_COMPONENT_TYPE(type, ctype, name) \ template <> \ struct mitk::MapPixelComponentType \ { \ static const int value = ctype; \ } template <> \ std::string mitk::PixelComponentTypeToString() \ { \ return name; \ } namespace mitk { - static const int PixelUserType = itk::ImageIOBase::MATRIX + 1; - static const int PixelComponentUserType = itk::ImageIOBase::DOUBLE + 1; + static const int PixelUserType = static_cast(itk::IOPixelEnum::MATRIX) + 1; + static const int PixelComponentUserType = static_cast(itk::IOComponentEnum::DOUBLE) + 1; /** * Maps pixel component types (primitive types like int, short, double, etc. and custom * types) to and integer constant. Specialize this template for custom types by using the * #MITK_PIXEL_COMPONENT_TYPE macro. */ template struct MapPixelComponentType { - static const int value = itk::ImageIOBase::MapPixelType::CType; + static const itk::IOComponentEnum value = itk::ImageIOBase::MapPixelType::CType; }; /** \brief This is an implementation of a type trait to provide a compile-time check for PixelType used in the instantiation of an itk::Image */ template struct isPrimitiveType { static const bool value = false; }; /** \brief Provides a partial specialization for the \sa isPrimitiveType object */ #define DEFINE_TYPE_PRIMITIVE(_TYPEIN) \ template <> \ struct isPrimitiveType<_TYPEIN> \ { \ static const bool value = true; \ } /** \brief Partial specialization (unsigned char) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(unsigned char); /** \brief Partial specialization (char) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(char); /** \brief Partial specialization (signed char) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(signed char); /** \brief Partial specialization (unsigned short) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(unsigned short); /** \brief Partial specialization (short) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(short); /** \brief Partial specialization (unsigned int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(unsigned int); /** \brief Partial specialization (int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(int); /** \brief Partial specialization (long int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(long int); /** \brief Partial specialization (long unsigned int) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(long unsigned int); /** \brief Partial specialization (float) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(float); /** \brief Partial specialization (double) for the isPrimitiveType object */ DEFINE_TYPE_PRIMITIVE(double); template struct ImageTypeTrait { typedef itk::Image ImageType; static const bool IsVectorImage = false; }; template struct ImageTypeTrait, VDimension> { typedef itk::VectorImage ImageType; static const bool IsVectorImage = true; }; template struct ImageTypeTrait { typedef T ImageType; static const bool IsVectorImage = false; }; template struct ImageTypeTrait, 0> { typedef itk::VectorImage ImageType; static const bool IsVectorImage = true; }; /** \brief Compile-time trait for resolving the ValueType from an ItkImageType */ template struct PixelTypeTrait { typedef T ValueType; }; /** \brief Partial specialization for the PixelTypeTrait * * Specialization for the false value. Used to define the value type for non-primitive pixel types */ template struct PixelTypeTrait { typedef typename T::ValueType ValueType; }; /** \brief Compile time resolving of the type of a component */ template struct GetComponentType { typedef typename PixelTypeTrait::value, T>::ValueType ComponentType; }; /** \brief Object for compile-time resolving of the number of components for given type. * * Default value for the component number is 1 */ template struct ComponentsTrait { static const size_t Size = 1; }; /** \brief Partial specialization for the ComponentsTraits in case of compound types */ template struct ComponentsTrait { static const size_t Size = T::ValueType::Length; }; - typedef itk::ImageIOBase::IOPixelType itkIOPixelType; + typedef itk::IOPixelEnum itkIOPixelType; + typedef itk::IOComponentEnum itkIOComponentType; /** \brief Object for compile-time translation of a composite pixel type into an itk::ImageIOBase::IOPixelType * information * * The default value of the IOCompositeType is the UNKNOWNPIXELTYPE, the default value will be used for all but the * types below with own partial specialization. The values of the IOCompositeType member in the specializations * correspond * to the values of the itk::ImageIOBase::IOPixelType enum values. */ template struct MapCompositePixelType { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::UNKNOWNPIXELTYPE; + static const itkIOPixelType IOCompositeType = itkIOPixelType::UNKNOWNPIXELTYPE; }; //------------------------ // Partial template specialization for fixed-length types //------------------------ template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::RGB; + static const itkIOPixelType IOCompositeType = itkIOPixelType::RGB; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::RGBA; + static const itkIOPixelType IOCompositeType = itkIOPixelType::RGBA; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::DIFFUSIONTENSOR3D; + static const itkIOPixelType IOCompositeType = itkIOPixelType::DIFFUSIONTENSOR3D; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::VECTOR; + static const itkIOPixelType IOCompositeType = itkIOPixelType::VECTOR; }; //------------------------ // Partial template specialization for variable-length types //------------------------ template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::VECTOR; + static const itkIOPixelType IOCompositeType = itkIOPixelType::VECTOR; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::COVARIANTVECTOR; + static const itkIOPixelType IOCompositeType = itkIOPixelType::COVARIANTVECTOR; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::COVARIANTVECTOR; + static const itkIOPixelType IOCompositeType = itkIOPixelType::COVARIANTVECTOR; }; template struct MapCompositePixelType> { - static const itkIOPixelType IOCompositeType = itk::ImageIOBase::MATRIX; + static const itkIOPixelType IOCompositeType = itkIOPixelType::MATRIX; }; /** \brief Object for compile-time translation of a pixel type into an itk::ImageIOBase::IOPixelType information * * The first template parameter is the pixel type to be translated, the second parameter determines the processing * way. For non-primitive types the first template parameter is passed to the MapCompositePixelType object to be * resolved there * for primitive types the value is set to SCALAR. * * To initalize the flag correctly in compile-time use the \sa isPrimitiveType trait. */ template struct MapPixelType { static const itkIOPixelType IOPixelType = MapCompositePixelType::IOCompositeType; - static const int IOComponentType = MapPixelComponentType::ComponentType>::value; + static const itkIOComponentType IOComponentType = MapPixelComponentType::ComponentType>::value; }; /** \brief Partial specialization for setting the IOPixelType for primitive types to SCALAR */ template struct MapPixelType { - static const itkIOPixelType IOPixelType = itk::ImageIOBase::SCALAR; - static const int IOComponentType = MapPixelComponentType::value; + static const itkIOPixelType IOPixelType = itkIOPixelType::SCALAR; + static const itkIOComponentType IOComponentType = MapPixelComponentType::value; }; } // end namespace mitk #endif // PIXELTYPETRAITS_H diff --git a/Modules/Core/include/mitkPointSet.h b/Modules/Core/include/mitkPointSet.h index 4a55101604..d58c5bfe77 100755 --- a/Modules/Core/include/mitkPointSet.h +++ b/Modules/Core/include/mitkPointSet.h @@ -1,348 +1,348 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKPointSet_H_HEADER_INCLUDED #define MITKPointSet_H_HEADER_INCLUDED #include "mitkBaseData.h" #include #include namespace mitk { /** * \brief Data structure which stores a set of points. * * 3D points are grouped within a point set; for time resolved usage, one point * set is created and maintained per time step. A point entry consists of the * point coordinates and point data. * * The point data includes a point ID (unique identifier to address this point * within the point set), the selection state of the point and the type of * the point. * * For further information about different point types see * mitk::PointSpecificationType in mitkVector.h. * * Inserting a point is accompanied by an event, containing an index. The new * point is inserted into the list at the specified position. At the same time * an internal ID is generated and stored for the point. Points at specific time * steps are accessed by specifying the time step number (which defaults to 0). * * The points of itk::PointSet stores the points in a pointContainer * (MapContainer). The points are best accessed by using a ConstIterator (as * defined in MapContainer); avoid access via index. * * The class internally uses an itk::Mesh for each time step. * * \section mitkPointSetDisplayOptions * * The default mappers for this data structure are mitk::PointSetGLMapper2D and * mitk::PointSetVtkMapper3D. See these classes for display options which can * can be set via properties. * * \section Events * * PointSet issues the following events, for which observers can register * (the below events are grouped into a class hierarchy as indicated by * identation level; e.g. PointSetSizeChangeEvent comprises PointSetAddEvent * and PointSetRemoveEvent): * * * PointSetEvent subsumes all PointSet events * PointSetMoveEvent issued when a point of the PointSet is moved * PointSetSizeChangeEvent subsumes add and remove events * PointSetAddEvent issued when a point is added to the PointSet * PointSetRemoveEvent issued when a point is removed from the PointSet * * \ingroup PSIO * \ingroup Data */ class MITKCORE_EXPORT PointSet : public BaseData { public: mitkClassMacro(PointSet, BaseData); itkFactorylessNewMacro(Self); itkCloneMacro(Self); typedef mitk::ScalarType CoordinateType; typedef mitk::ScalarType InterpolationWeightType; static const unsigned int PointDimension = 3; static const unsigned int MaxTopologicalDimension = 3; /** * \brief struct for data of a point */ struct MITKCORE_EXPORT PointDataType { unsigned int id; // to give the point a special ID bool selected; // information about if the point is selected mitk::PointSpecificationType pointSpec; // specifies the type of the point bool operator==(const PointDataType &other) const; }; /** * \brief cellDataType, that stores all indexes of the lines, that are * selected e.g.: points A,B and C.Between A and B there is a line with * index 0. If vector of cellData contains 1 and 2, then the lines between * B and C and C and A is selected. */ typedef std::vector SelectedLinesType; typedef SelectedLinesType::iterator SelectedLinesIter; struct CellDataType { // used to set the whole cell on selected bool selected; // indexes of selected lines. 0 is between pointId 0 and 1 SelectedLinesType selectedLines; // is the polygon already finished and closed bool closed; }; typedef itk::DefaultDynamicMeshTraits MeshTraits; typedef itk::Mesh MeshType; typedef MeshType DataType; typedef Point3D PointType; typedef DataType::PointIdentifier PointIdentifier; typedef DataType::PointsContainer PointsContainer; typedef DataType::PointsContainerIterator PointsIterator; typedef DataType::PointsContainer::ConstIterator PointsConstIterator; typedef DataType::PointDataContainer PointDataContainer; typedef DataType::PointDataContainerIterator PointDataIterator; typedef DataType::PointDataContainerIterator PointDataConstIterator; void Expand(unsigned int timeSteps) override; /** \brief executes the given Operation */ void ExecuteOperation(Operation *operation) override; /** \brief returns the current size of the point-list */ virtual int GetSize(unsigned int t = 0) const; virtual unsigned int GetPointSetSeriesSize() const; /** \brief returns the pointset */ virtual DataType::Pointer GetPointSet(int t = 0) const; PointsIterator Begin(int t = 0); PointsConstIterator Begin(int t = 0) const; PointsIterator End(int t = 0); PointsConstIterator End(int t = 0) const; /** * \brief Get an iterator to the max ID element if existent. Return End() otherwise. */ PointsIterator GetMaxId(int t = 0); /** * \brief Get the point with ID id in world coordinates * * check if the ID exists. If it doesn't exist, then return 0,0,0 */ PointType GetPoint(PointIdentifier id, int t = 0) const; /** * \brief Get the point with ID id in world coordinates * * If a point exists for the ID id, the point is returned in the parameter point * and the method returns true. If the ID does not exist, the method returns false */ bool GetPointIfExists(PointIdentifier id, PointType *point, int t = 0) const; /** * \brief Set the given point in world coordinate system into the itkPointSet. */ void SetPoint(PointIdentifier id, PointType point, int t = 0); /** * \brief Set the given point in world coordinate system with the given PointSpecificationType */ void SetPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t = 0); /** * \brief Set the given point in world coordinate system into the itkPointSet. */ void InsertPoint(PointIdentifier id, PointType point, int t = 0); /** * \brief Set the given point in world coordinate system with given PointSpecificationType */ void InsertPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t); /** * \brief Insert the given point in world coordinate system with incremented max id at time step t. */ PointIdentifier InsertPoint(PointType point, int t = 0); /** * \brief Remove point with given id at timestep t, if existent */ bool RemovePointIfExists(PointIdentifier id, int t = 0); /** * \brief Remove max id point at timestep t and return iterator to precedent point */ PointsIterator RemovePointAtEnd(int t = 0); /** * \brief Swap a point at the given position (id) with the upper point (moveUpwards=true) or with the lower point * (moveUpwards=false). * If upper or lower index does not exist false is returned, if swap was successful true. */ bool SwapPointPosition(PointIdentifier id, bool moveUpwards, int t = 0); /** * \brief searches a selected point and returns the id of that point. * If no point is found, then -1 is returned */ virtual int SearchSelectedPoint(int t = 0) const; /** \brief returns true if a point exists at this position */ virtual bool IndexExists(int position, int t = 0) const; /** \brief to get the state selected/unselected of the point on the * position */ virtual bool GetSelectInfo(int position, int t = 0) const; virtual void SetSelectInfo(int position, bool selected, int t = 0); /** \brief to get the type of the point at the position and the moment */ virtual PointSpecificationType GetSpecificationTypeInfo(int position, int t) const; /** \brief returns the number of selected points */ virtual int GetNumberOfSelected(int t = 0) const; /** * \brief searches a point in the list == point +/- distance * * \param point is in world coordinates. * \param distance is in mm. * \param t * returns -1 if no point is found * or the position in the list of the first match */ int SearchPoint(Point3D point, ScalarType distance, int t = 0) const; bool IsEmptyTimeStep(unsigned int t) const override; // virtual methods, that need to be implemented void UpdateOutputInformation() override; void SetRequestedRegionToLargestPossibleRegion() override; bool RequestedRegionIsOutsideOfTheBufferedRegion() override; bool VerifyRequestedRegion() override; void SetRequestedRegion(const itk::DataObject *data) override; // Method for subclasses virtual void OnPointSetChange(){}; protected: mitkCloneMacro(Self); PointSet(); PointSet(const PointSet &other); ~PointSet() override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; ///< print content of the object to os void ClearData() override; void InitializeEmpty() override; /** \brief swaps point coordinates and point data of the points with identifiers id1 and id2 */ bool SwapPointContents(PointIdentifier id1, PointIdentifier id2, int t = 0); typedef std::vector PointSetSeries; PointSetSeries m_PointSetSeries; DataType::PointsContainer::Pointer m_EmptyPointsContainer; /** * @brief flag to indicate the right time to call SetBounds **/ bool m_CalculateBoundingBox; }; /** * @brief Equal A function comparing two pointsets for beeing identical. * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const * mitk::PointSet& p1, const mitk::PointSet& p2) instead. * * @ingroup MITKTestingAPI * * The function compares the Geometry, the size and all points element-wise. * The parameter eps is a tolarence value for all methods which are internally used for comparion. * * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @param checkGeometry if comparing point sets loaded from a file, the geometries might be different and must not be * compared. In all other cases, you should compare the geometries. * @return True, if all subsequent comparisons are true, false otherwise */ DEPRECATED(MITKCORE_EXPORT bool Equal(const mitk::PointSet *leftHandSide, const mitk::PointSet *rightHandSide, mitk::ScalarType eps, bool verbose, bool checkGeometry = true)); /** * @brief Equal A function comparing two pointsets for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the Geometry, the size and all points element-wise. * The parameter eps is a tolarence value for all methods which are internally used for comparion. * * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @param checkGeometry if comparing point sets loaded from a file, the geometries might be different and must not be * compared. In all other cases, you should compare the geometries. * @return True, if all subsequent comparisons are true, false otherwise */ MITKCORE_EXPORT bool Equal(const mitk::PointSet &leftHandSide, const mitk::PointSet &rightHandSide, mitk::ScalarType eps, bool verbose, bool checkGeometry = true); - itkEventMacro(PointSetEvent, itk::AnyEvent); - itkEventMacro(PointSetMoveEvent, PointSetEvent); - itkEventMacro(PointSetSizeChangeEvent, PointSetEvent); - itkEventMacro(PointSetAddEvent, PointSetSizeChangeEvent); - itkEventMacro(PointSetRemoveEvent, PointSetSizeChangeEvent); - itkEventMacro(PointSetExtendTimeRangeEvent, PointSetEvent); + itkEventMacroDeclaration(PointSetEvent, itk::AnyEvent); + itkEventMacroDeclaration(PointSetMoveEvent, PointSetEvent); + itkEventMacroDeclaration(PointSetSizeChangeEvent, PointSetEvent); + itkEventMacroDeclaration(PointSetAddEvent, PointSetSizeChangeEvent); + itkEventMacroDeclaration(PointSetRemoveEvent, PointSetSizeChangeEvent); + itkEventMacroDeclaration(PointSetExtendTimeRangeEvent, PointSetEvent); } // namespace mitk #endif /* MITKPointSet_H_HEADER_INCLUDED */ diff --git a/Modules/Core/include/mitkPropertyList.h b/Modules/Core/include/mitkPropertyList.h index d818815efd..c5832a5888 100644 --- a/Modules/Core/include/mitkPropertyList.h +++ b/Modules/Core/include/mitkPropertyList.h @@ -1,254 +1,254 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D #define PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D #include "mitkBaseProperty.h" #include "mitkGenericProperty.h" #include "mitkUIDGenerator.h" #include "mitkIPropertyOwner.h" #include #include #include #include namespace mitk { class XMLWriter; /** * @brief Key-value list holding instances of BaseProperty * * This list is meant to hold an arbitrary list of "properties", * which should describe the object associated with this list. * * Usually you will use PropertyList as part of a DataNode * object - in this context the properties describe the data object * held by the DataNode (e.g. whether the object is rendered at * all, which color is used for rendering, what name should be * displayed for the object, etc.) * * The values in the list are not fixed, you may introduce any kind * of property that seems useful - all you have to do is inherit * from BaseProperty. * * The list is organized as a key-value pairs, i.e. * * \li "name" : pointer to a StringProperty * \li "visible" : pointer to a BoolProperty * \li "color" : pointer to a ColorProperty * \li "volume" : pointer to a FloatProperty * * Please see the documentation of SetProperty and ReplaceProperty for two * quite different semantics. Normally SetProperty is what you want - this * method will try to change the value of an existing property and will * not allow you to replace e.g. a ColorProperty with an IntProperty. * * Please also regard, that the key of a property must be a none empty string. * This is a precondition. Setting properties with empty keys will raise an exception. * * @ingroup DataManagement */ class MITKCORE_EXPORT PropertyList : public itk::Object, public IPropertyOwner { public: mitkClassMacroItkParent(PropertyList, itk::Object); /** * Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Map structure to hold the properties: the map key is a string, * the value consists of the actual property object (BaseProperty). */ typedef std::map PropertyMap; typedef std::pair PropertyMapElementType; // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; /** * @brief Get a property by its name. */ mitk::BaseProperty *GetProperty(const std::string &propertyKey) const; /** * @brief Set a property object in the list/map by reference. * * The actual OBJECT holding the value of the property is replaced by this function. * This is useful if you want to change the type of the property, like from BoolProperty to StringProperty. * Another use is to share one and the same property object among several ProperyList/DataNode objects, which * makes them appear synchronized. */ void ReplaceProperty(const std::string &propertyKey, BaseProperty *property); /** * @brief Set a property object in the list/map by reference. */ void ConcatenatePropertyList(PropertyList *pList, bool replace = false); //##Documentation //## @brief Convenience access method for GenericProperty properties //## (T being the type of the second parameter) //## @return @a true property was found template bool GetPropertyValue(const char *propertyKey, T &value) const { GenericProperty *gp = dynamic_cast *>(GetProperty(propertyKey)); if (gp != nullptr) { value = gp->GetValue(); return true; } return false; } /** * @brief Convenience method to access the value of a BoolProperty */ bool GetBoolProperty(const char *propertyKey, bool &boolValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, bool &boolValue) const; /** * @brief Convenience method to set the value of a BoolProperty */ void SetBoolProperty(const char *propertyKey, bool boolValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, bool boolValue); /** * @brief Convenience method to access the value of an IntProperty */ bool GetIntProperty(const char *propertyKey, int &intValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, int &intValue) const; /** * @brief Convenience method to set the value of an IntProperty */ void SetIntProperty(const char *propertyKey, int intValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, int intValue); /** * @brief Convenience method to access the value of a FloatProperty */ bool GetFloatProperty(const char *propertyKey, float &floatValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, float &floatValue) const; /** * @brief Convenience method to set the value of a FloatProperty */ void SetFloatProperty(const char *propertyKey, float floatValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, float floatValue); /** * @brief Convenience method to access the value of a DoubleProperty */ bool GetDoubleProperty(const char *propertyKey, double &doubleValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, double &doubleValue) const; /** * @brief Convenience method to set the value of a DoubleProperty */ void SetDoubleProperty(const char *propertyKey, double doubleValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, double doubleValue); /** * @brief Convenience method to access the value of a StringProperty */ bool GetStringProperty(const char *propertyKey, std::string &stringValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, std::string &stringValue) const; /** * @brief Convenience method to set the value of a StringProperty */ void SetStringProperty(const char *propertyKey, const char *stringValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, const char *stringValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, const std::string &stringValue); /** * @brief Get the timestamp of the last change of the map or the last change of one of * the properties store in the list (whichever is later). */ - unsigned long GetMTime() const override; + itk::ModifiedTimeType GetMTime() const override; /** * @brief Remove a property from the list/map. */ bool DeleteProperty(const std::string &propertyKey); const PropertyMap *GetMap() const { return &m_Properties; } bool IsEmpty() const { return m_Properties.empty(); } virtual void Clear(); protected: PropertyList(); PropertyList(const PropertyList &other); ~PropertyList() override; /** * @brief Map of properties. */ PropertyMap m_Properties; private: itk::LightObject::Pointer InternalClone() const override; }; } // namespace mitk #endif /* PROPERTYLIST_H_HEADER_INCLUDED_C1C77D8D */ diff --git a/Modules/Core/include/mitkRenderingManager.h b/Modules/Core/include/mitkRenderingManager.h index 37f1906f2e..0e67583450 100644 --- a/Modules/Core/include/mitkRenderingManager.h +++ b/Modules/Core/include/mitkRenderingManager.h @@ -1,495 +1,495 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKRENDERINGMANAGER_H #define MITKRENDERINGMANAGER_H #include #include #include #include #include #include #include #include class vtkRenderWindow; class vtkObject; namespace mitk { class RenderingManagerFactory; class BaseGeometry; class SliceNavigationController; class BaseRenderer; class DataStorage; /** * \brief Manager for coordinating the rendering process. * * RenderingManager is a central instance retrieving and executing * RenderWindow update requests. Its main purpose is to coordinate * distributed requests which cannot be aware of each other - lacking the * knowledge of whether they are really necessary or not. For example, two * objects might determine that a specific RenderWindow needs to be updated. * This would result in one unnecessary update, if both executed the update * on their own. * * The RenderingManager addresses this by letting each such object * request an update, and waiting for other objects to possibly * issue the same request. The actual update will then only be executed at a * well-defined point in the main event loop (this may be each time after * event processing is done). * * Convinience methods for updating all RenderWindows which have been * registered with the RenderingManager exist. If theses methods are not * used, it is not required to register (add) RenderWindows prior to using * the RenderingManager. * * The methods #ForceImmediateUpdate() and #ForceImmediateUpdateAll() can * be used to force the RenderWindow update execution without any delay, * bypassing the request functionality. * * The interface of RenderingManager is platform independent. Platform * specific subclasses have to be implemented, though, to supply an * appropriate event issueing for controlling the update execution process. * See method documentation for a description of how this can be done. * * \sa TestingRenderingManager An "empty" RenderingManager implementation which * can be used in tests etc. * */ class MITKCORE_EXPORT RenderingManager : public itk::Object { public: mitkClassMacroItkParent(RenderingManager, itk::Object); typedef std::vector RenderWindowVector; typedef std::vector FloatVector; typedef std::vector BoolVector; typedef itk::SmartPointer DataStoragePointer; enum RequestType { REQUEST_UPDATE_ALL = 0, REQUEST_UPDATE_2DWINDOWS, REQUEST_UPDATE_3DWINDOWS }; static Pointer New(); /** Set the object factory which produces the desired platform specific * RenderingManager singleton instance. */ static void SetFactory(RenderingManagerFactory *factory); /** Get the object factory which produces the platform specific * RenderingManager instances. */ static const RenderingManagerFactory *GetFactory(); /** Returns true if a factory has already been set. */ static bool HasFactory(); /** Get the RenderingManager singleton instance. */ static RenderingManager *GetInstance(); /** Returns true if the singleton instance does already exist. */ static bool IsInstantiated(); /** Adds a RenderWindow. This is required if the methods #RequestUpdateAll * or #ForceImmediateUpdate are to be used. */ void AddRenderWindow(vtkRenderWindow *renderWindow); /** Removes a RenderWindow. */ void RemoveRenderWindow(vtkRenderWindow *renderWindow); /** Get a list of all registered RenderWindows */ const RenderWindowVector &GetAllRegisteredRenderWindows(); /** Requests an update for the specified RenderWindow, to be executed as * soon as the main loop is ready for rendering. */ void RequestUpdate(vtkRenderWindow *renderWindow); /** Immediately executes an update of the specified RenderWindow. */ void ForceImmediateUpdate(vtkRenderWindow *renderWindow); /** Requests all currently registered RenderWindows to be updated. * If only 2D or 3D windows should be updated, this can be specified * via the parameter requestType. */ void RequestUpdateAll(RequestType type = REQUEST_UPDATE_ALL); /** Immediately executes an update of all registered RenderWindows. * If only 2D or 3D windows should be updated, this can be specified * via the parameter requestType. */ void ForceImmediateUpdateAll(RequestType type = REQUEST_UPDATE_ALL); /** * @brief Initialize the render windows by the aggregated geometry of all objects that are held in * the data storage. * * @param dataStorage The data storage from which the bounding object can be retrieved */ virtual void InitializeViewsByBoundingObjects(const DataStorage* dataStorage); /** * @brief Initialize the render windows specified by "type" to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * * @param geometry The geometry to be used to initialize / update a * render window's time and slice navigation controller * @param type The type of update request: * - REQUEST_UPDATE_ALL will initialize / update the * time and slice navigation controller of all retrieved render windows * - REQUEST_UPDATE_2DWINDOWS will only initialize / update the * time and slice navigation controller of 2D render windows * - REQUEST_UPDATE_3DWINDOWS will only initialize / update the * time and slice navigation controller of 3D render windows * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeViews(const BaseGeometry *geometry, RequestType type = REQUEST_UPDATE_ALL, bool resetCamera = true); /** * @brief Initialize the render windows specified by "type" to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * * @param geometry The geometry to be used to initialize / update a * render window's time- and slice navigation controller * @param type The type of update request: * - REQUEST_UPDATE_ALL will initialize / update the * time- and slice navigation controller of all retrieved render windows * - REQUEST_UPDATE_2DWINDOWS will only initialize / update the * time- and slice navigation controller of 2D render windows * - REQUEST_UPDATE_3DWINDOWS will only initialize / update the * time- and slice navigation controller of 3D render windows * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeViews(const TimeGeometry *geometry, RequestType type = REQUEST_UPDATE_ALL, bool resetCamera = true); /** * @brief Initialize the render windows specified by "type" to the default viewing direction * without updating the geometry information. * * @param type The type of update request: * - REQUEST_UPDATE_ALL will initialize the * slice navigation controller of all retrieved render windows * - REQUEST_UPDATE_2DWINDOWS will only initialize the * slice navigation controller of 2D render windows * - REQUEST_UPDATE_3DWINDOWS will only initialize the * slice navigation controller of 3D render windows */ virtual bool InitializeViews(RequestType type = REQUEST_UPDATE_ALL); /** * @brief Initialize the specified render window to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * * @param renderWindow The specifid render window to update * @param geometry The geometry to be used to initialize / update the * render window's time- and slice navigation controller * @param initializeGlobalTime If this parameter is set to true, the time navigation controller will be * initialized / updated with the given geometry. If set to false, the geometry * of the time navigation controller is not updated. * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeView(vtkRenderWindow *renderWindow, const BaseGeometry *geometry, bool initializeGlobalTime = true, bool resetCamera = true); /** * @brief Initialize the specified render window to the given geometry. * * Throws an exception if bounding box has 0 extent due to exceeding double precision range. * * @param renderWindow The specifid render window to update * @param geometry The geometry to be used to initialize / update the * render window's time- and slice navigation controller * @param initializeGlobalTime If this parameter is set to true, the time navigation controller will be * initialized / updated with the given geometry. If set to false, the geometry * of the time navigation controller is not updated. * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ virtual bool InitializeView(vtkRenderWindow *renderWindow, const TimeGeometry *geometry, bool initializeGlobalTime = true, bool resetCamera = true); /** * @brief Initialize the specified render window to the default viewing direction * without updating the geometry information. * * @param renderWindow The specifid render window to update */ virtual bool InitializeView(vtkRenderWindow *renderWindow); /** Gets the (global) SliceNavigationController responsible for * time-slicing. */ const SliceNavigationController *GetTimeNavigationController() const; /** Gets the (global) SliceNavigationController responsible for * time-slicing. */ SliceNavigationController *GetTimeNavigationController(); ~RenderingManager() override; /** Executes all pending requests. This method has to be called by the * system whenever a RenderingManager induced request event occurs in * the system pipeline (see concrete RenderingManager implementations). */ virtual void ExecutePendingRequests(); bool IsRendering() const; void AbortRendering(); /** En-/Disable LOD increase globally. */ itkSetMacro(LODIncreaseBlocked, bool); /** En-/Disable LOD increase globally. */ itkGetMacro(LODIncreaseBlocked, bool); /** En-/Disable LOD increase globally. */ itkBooleanMacro(LODIncreaseBlocked); /** En-/Disable LOD abort mechanism. */ itkSetMacro(LODAbortMechanismEnabled, bool); /** En-/Disable LOD abort mechanism. */ itkGetMacro(LODAbortMechanismEnabled, bool); /** En-/Disable LOD abort mechanism. */ itkBooleanMacro(LODAbortMechanismEnabled); /** Force a sub-class to start a timer for a pending hires-rendering request */ virtual void StartOrResetTimer(){}; /** To be called by a sub-class from a timer callback */ void ExecutePendingHighResRenderingRequest(); virtual void DoStartRendering(){}; virtual void DoMonitorRendering(){}; virtual void DoFinishAbortRendering(){}; int GetNextLOD(BaseRenderer *renderer); /** Set current LOD (nullptr means all renderers)*/ void SetMaximumLOD(unsigned int max); void SetShading(bool state, unsigned int lod); bool GetShading(unsigned int lod); void SetClippingPlaneStatus(bool status); bool GetClippingPlaneStatus(); void SetShadingValues(float ambient, float diffuse, float specular, float specpower); FloatVector &GetShadingValues(); /** Returns a property list */ PropertyList::Pointer GetPropertyList() const; /** Returns a property from m_PropertyList */ BaseProperty *GetProperty(const char *propertyKey) const; /** Sets or adds (if not present) a property in m_PropertyList */ void SetProperty(const char *propertyKey, BaseProperty *propertyValue); /** * \brief Setter for internal DataStorage * * Sets the DataStorage that is used internally. This instance holds all DataNodes that are * rendered by the registered BaseRenderers. * * If this DataStorage is changed at runtime by calling SetDataStorage(), * all currently registered BaseRenderers are automatically given the correct instance. * When a new BaseRenderer is added, it is automatically initialized with the currently active DataStorage. */ void SetDataStorage(DataStorage *storage); /** * \brief Getter for internal DataStorage * * Returns the DataStorage that is used internally. This instance holds all DataNodes that are * rendered by the registered BaseRenderers. */ itkGetMacro(DataStorage, DataStorage*); itkGetConstMacro(DataStorage, DataStorage*); /** * @brief Sets a flag to the given renderwindow to indicated that it has the focus e.g. has been clicked recently. * @param focusWindow */ void SetRenderWindowFocus(vtkRenderWindow *focusWindow); itkGetMacro(FocusedRenderWindow, vtkRenderWindow *); itkSetMacro(ConstrainedPanningZooming, bool); itkGetConstMacro(ConstrainedPanningZooming, bool); void SetAntiAliasing(AntiAliasing antiAliasing); itkGetConstMacro(AntiAliasing, AntiAliasing); protected: enum { RENDERING_INACTIVE = 0, RENDERING_REQUESTED, RENDERING_INPROGRESS }; RenderingManager(); /** Abstract method for generating a system specific event for rendering * request. This method is called whenever an update is requested */ virtual void GenerateRenderingRequestEvent() = 0; virtual void InitializePropertyList(); bool m_UpdatePending; typedef std::map RendererIntMap; typedef std::map RendererBoolMap; RendererBoolMap m_RenderingAbortedMap; RendererIntMap m_NextLODMap; unsigned int m_MaxLOD; bool m_LODIncreaseBlocked; bool m_LODAbortMechanismEnabled; BoolVector m_ShadingEnabled; bool m_ClippingPlaneEnabled; FloatVector m_ShadingValues; static void RenderingStartCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata); static void RenderingProgressCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata); static void RenderingEndCallback(vtkObject *caller, unsigned long eid, void *clientdata, void *calldata); typedef std::map RenderWindowList; RenderWindowList m_RenderWindowList; RenderWindowVector m_AllRenderWindows; struct RenderWindowCallbacks { vtkCallbackCommand *commands[3u]; }; typedef std::map RenderWindowCallbacksList; RenderWindowCallbacksList m_RenderWindowCallbacksList; itk::SmartPointer m_TimeNavigationController; static RenderingManager::Pointer s_Instance; static RenderingManagerFactory *s_RenderingManagerFactory; PropertyList::Pointer m_PropertyList; DataStoragePointer m_DataStorage; bool m_ConstrainedPanningZooming; private: /** * @brief Initialize the specified renderer to the given geometry. * * @param baseRenderer The specifid renderer to update * @param geometry The geometry to be used to initialize / update the * render window's slice navigation controller * @param boundingBoxInitialized If this parameter is set to true, the slice navigation controller will be * initialized / updated with the given geometry. If set to false, the geometry * of the slice navigation controller is not updated. * @param mapperID The mapper ID is used to define if the given renderer is a 2D or a 3D renderer. * In case of a 2D renderer and if "boundingBoxInitialized" is set to true (slice * navigation controller will be updated with a new geometry), the position of the * slice navigation controller is set to the center slice. * @param resetCamera If this parameter is set to true, the camera controller will be * set / fit to the center of the rendered image. If set to false, only the * the slice navigation controller is reset to the geometry without changing * the camera view / position. */ void InternalViewInitialization(BaseRenderer *baseRenderer, const TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID, bool resetCamera); /** * @brief Extend the bounding box of the given geometry to make sure the bounding box has an extent bigger than * zero in any direction. * * @param originalGeometry The original geometry to be extended * @param modifiedGeometry The modified geometry where the new bounds (extended bounding box) are used / set */ bool ExtendGeometryForBoundingBox(const TimeGeometry* originalGeometry, TimeGeometry::Pointer& modifiedGeometry); vtkRenderWindow *m_FocusedRenderWindow; AntiAliasing m_AntiAliasing; }; #pragma GCC visibility push(default) - itkEventMacro(RenderingManagerEvent, itk::AnyEvent); - itkEventMacro(RenderingManagerViewsInitializedEvent, RenderingManagerEvent); + itkEventMacroDeclaration(RenderingManagerEvent, itk::AnyEvent); + itkEventMacroDeclaration(RenderingManagerViewsInitializedEvent, RenderingManagerEvent); #pragma GCC visibility pop itkEventMacroDeclaration(FocusChangedEvent, itk::AnyEvent); /** * Generic RenderingManager implementation for "non-rendering-plattform", * e.g. for tests. Its factory (TestingRenderingManagerFactory) is * automatically on start-up and is used by default if not other * RenderingManagerFactory is instantiated explicitly thereafter. * (see mitkRenderingManager.cpp) */ class MITKCORE_EXPORT TestingRenderingManager : public RenderingManager { public: mitkClassMacro(TestingRenderingManager, RenderingManager); itkFactorylessNewMacro(Self); itkCloneMacro(Self); protected: void GenerateRenderingRequestEvent() override {}; }; } // namespace mitk #endif // MITKRENDERINGMANAGER_H diff --git a/Modules/Core/include/mitkSliceNavigationController.h b/Modules/Core/include/mitkSliceNavigationController.h index a7169a6274..113f51cdd1 100644 --- a/Modules/Core/include/mitkSliceNavigationController.h +++ b/Modules/Core/include/mitkSliceNavigationController.h @@ -1,477 +1,472 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F #define SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F #include "mitkBaseController.h" #include "mitkMessage.h" #include "mitkRenderingManager.h" #include "mitkTimeGeometry.h" #include #pragma GCC visibility push(default) #include #pragma GCC visibility pop #include "mitkDataStorage.h" #include "mitkRestorePlanePositionOperation.h" #include #include namespace mitk { - #define mitkTimeGeometryEventMacro(classname, super) \ class MITKCORE_EXPORT classname : public super \ { \ public: \ typedef classname Self; \ typedef super Superclass; \ classname(TimeGeometry *aTimeGeometry, unsigned int aPos) : Superclass(aTimeGeometry, aPos) {} \ virtual ~classname() {} \ virtual const char *GetEventName() const { return #classname; } \ virtual bool CheckEvent(const ::itk::EventObject *e) const { return dynamic_cast(e); } \ virtual ::itk::EventObject *MakeObject() const { return new Self(GetTimeGeometry(), GetPos()); } \ private: \ void operator=(const Self &); \ } class PlaneGeometry; class BaseGeometry; class BaseRenderer; /** * \brief Controls the selection of the slice the associated BaseRenderer * will display * * A SliceNavigationController takes a BaseGeometry or a TimeGeometry as input world geometry * (TODO what are the exact requirements?) and generates a TimeGeometry * as output. The TimeGeometry holds a number of SlicedGeometry3Ds and * these in turn hold a series of PlaneGeometries. One of these PlaneGeometries is * selected as world geometry for the BaseRenderers associated to 2D views. * * The SliceNavigationController holds has Steppers (one for the slice, a * second for the time step), which control the selection of a single * PlaneGeometry from the TimeGeometry. SliceNavigationController generates * ITK events to tell observers, like a BaseRenderer, when the selected slice * or timestep changes. * * Example: * \code * // Initialization * sliceCtrl = mitk::SliceNavigationController::New(); * * // Tell the navigator the geometry to be sliced (with geometry a * // BaseGeometry::ConstPointer) * sliceCtrl->SetInputWorldGeometry3D(geometry.GetPointer()); * * // Tell the navigator in which direction it shall slice the data * sliceCtrl->SetViewDirection(mitk::SliceNavigationController::Axial); * * // Connect one or more BaseRenderer to this navigator, i.e.: events sent * // by the navigator when stepping through the slices (e.g. by * // sliceCtrl->GetSlice()->Next()) will be received by the BaseRenderer * // (in this example only slice-changes, see also ConnectGeometryTimeEvent * // and ConnectGeometryEvents.) * sliceCtrl->ConnectGeometrySliceEvent(renderer.GetPointer()); * * //create a world geometry and send the information to the connected renderer(s) * sliceCtrl->Update(); * \endcode * * * You can connect visible navigators to a SliceNavigationController, e.g., a * QmitkSliderNavigator (for Qt): * * \code * // Create the visible navigator (a slider with a spin-box) * QmitkSliderNavigator* navigator = * new QmitkSliderNavigator(parent, "slidernavigator"); * * // Connect the navigator to the slice-stepper of the * // SliceNavigationController. For initialization (position, mininal and * // maximal values) the values of the SliceNavigationController are used. * // Thus, accessing methods of a navigator is normally not necessary, since * // everything can be set via the (Qt-independent) SliceNavigationController. * // The QmitkStepperAdapter converts the Qt-signals to Qt-independent * // itk-events. * new QmitkStepperAdapter(navigator, sliceCtrl->GetSlice(), "navigatoradaptor"); * \endcode * * If you do not want that all renderwindows are updated when a new slice is * selected, you can use a specific RenderingManager, which updates only those * renderwindows that should be updated. This is sometimes useful when a 3D view * does not need to be updated when the slices in some 2D views are changed. * QmitkSliderNavigator (for Qt): * * \code * // create a specific RenderingManager * mitk::RenderingManager::Pointer myManager = mitk::RenderingManager::New(); * * // tell the RenderingManager to update only renderwindow1 and renderwindow2 * myManager->AddRenderWindow(renderwindow1); * myManager->AddRenderWindow(renderwindow2); * * // tell the SliceNavigationController of renderwindow1 and renderwindow2 * // to use the specific RenderingManager instead of the global one * renderwindow1->GetSliceNavigationController()->SetRenderingManager(myManager); * renderwindow2->GetSliceNavigationController()->SetRenderingManager(myManager); * \endcode * * \todo implement for non-evenly-timed geometry! * \ingroup NavigationControl */ class MITKCORE_EXPORT SliceNavigationController : public BaseController { public: mitkClassMacro(SliceNavigationController, BaseController); // itkFactorylessNewMacro(Self) // mitkNewMacro1Param(Self, const char *); itkNewMacro(Self); // itkCloneMacro(Self) /** * \brief Possible view directions, \a Original will uses * the PlaneGeometry instances in a SlicedGeometry3D provided * as input world geometry (by SetInputWorldGeometry3D). */ enum ViewDirection { Axial, Sagittal, Frontal, Original }; /** * \brief Set the input world geometry3D out of which the * geometries for slicing will be created. * * Any previous previous set input geometry (3D or Time) will * be ignored in future. */ void SetInputWorldGeometry3D(const mitk::BaseGeometry *geometry); itkGetConstObjectMacro(InputWorldGeometry3D, mitk::BaseGeometry); void SetInputWorldTimeGeometry(const mitk::TimeGeometry *geometry); itkGetConstObjectMacro(InputWorldTimeGeometry, mitk::TimeGeometry); /** * \brief Access the created geometry */ itkGetConstObjectMacro(CreatedWorldGeometry, mitk::TimeGeometry); /** * \brief Set the desired view directions * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(ViewDirection, ViewDirection); itkGetEnumMacro(ViewDirection, ViewDirection); /** * \brief Set the default view direction * * This is used to re-initialize the view direction of the SNC to the * default value with SetViewDirectionToDefault() * * \sa ViewDirection * \sa Update(ViewDirection viewDirection, bool top = true, * bool frontside = true, bool rotated = false) */ itkSetEnumMacro(DefaultViewDirection, ViewDirection); itkGetEnumMacro(DefaultViewDirection, ViewDirection); const char *GetViewDirectionAsString() const; virtual void SetViewDirectionToDefault(); /** * \brief Do the actual creation and send it to the connected * observers (renderers) * */ virtual void Update(); /** * \brief Extended version of Update, additionally allowing to * specify the direction/orientation of the created geometry. * */ virtual void Update(ViewDirection viewDirection, bool top = true, bool frontside = true, bool rotated = false); /** * \brief Send the created geometry to the connected * observers (renderers) * * Called by Update(). */ virtual void SendCreatedWorldGeometry(); /** * \brief Tell observers to re-read the currently selected 2D geometry * */ virtual void SendCreatedWorldGeometryUpdate(); /** * \brief Send the currently selected slice to the connected * observers (renderers) * * Called by Update(). */ virtual void SendSlice(); /** * \brief Send the currently selected time to the connected * observers (renderers) * * Called by Update(). */ virtual void SendTime(); -#pragma GCC visibility push(default) - itkEventMacro(UpdateEvent, itk::AnyEvent); -#pragma GCC visibility pop - class MITKCORE_EXPORT TimeGeometryEvent : public itk::AnyEvent { public: typedef TimeGeometryEvent Self; typedef itk::AnyEvent Superclass; TimeGeometryEvent(TimeGeometry *aTimeGeometry, unsigned int aPos) : m_TimeGeometry(aTimeGeometry), m_Pos(aPos) {} ~TimeGeometryEvent() override {} const char *GetEventName() const override { return "TimeGeometryEvent"; } bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast(e); } ::itk::EventObject *MakeObject() const override { return new Self(m_TimeGeometry, m_Pos); } TimeGeometry *GetTimeGeometry() const { return m_TimeGeometry; } unsigned int GetPos() const { return m_Pos; } private: TimeGeometry::Pointer m_TimeGeometry; unsigned int m_Pos; // TimeGeometryEvent(const Self&); void operator=(const Self &); // just hide }; mitkTimeGeometryEventMacro(GeometrySendEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometryUpdateEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometryTimeEvent, TimeGeometryEvent); mitkTimeGeometryEventMacro(GeometrySliceEvent, TimeGeometryEvent); template void ConnectGeometrySendEvent(T *receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometry); unsigned long tag = AddObserver(GeometrySendEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometryUpdateEvent(T *receiver) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::UpdateGeometry); unsigned long tag = AddObserver(GeometryUpdateEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); } template void ConnectGeometrySliceEvent(T *receiver, bool connectSendEvent = true) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometrySlice); unsigned long tag = AddObserver(GeometrySliceEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); if (connectSendEvent) ConnectGeometrySendEvent(receiver); } template void ConnectGeometryTimeEvent(T *receiver, bool connectSendEvent = true) { typedef typename itk::ReceptorMemberCommand::Pointer ReceptorMemberCommandPointer; ReceptorMemberCommandPointer eventReceptorCommand = itk::ReceptorMemberCommand::New(); eventReceptorCommand->SetCallbackFunction(receiver, &T::SetGeometryTime); unsigned long tag = AddObserver(GeometryTimeEvent(nullptr, 0), eventReceptorCommand); m_ReceiverToObserverTagsMap[static_cast(receiver)].push_back(tag); if (connectSendEvent) ConnectGeometrySendEvent(receiver); } template void ConnectGeometryEvents(T *receiver) { // connect sendEvent only once ConnectGeometrySliceEvent(receiver, false); ConnectGeometryTimeEvent(receiver); } // use a templated method to get the right offset when casting to void* template void Disconnect(T *receiver) { auto i = m_ReceiverToObserverTagsMap.find(static_cast(receiver)); if (i == m_ReceiverToObserverTagsMap.end()) return; const std::list &tags = i->second; for (auto tagIter = tags.begin(); tagIter != tags.end(); ++tagIter) { RemoveObserver(*tagIter); } m_ReceiverToObserverTagsMap.erase(i); } Message1 SetCrosshairEvent; /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface * \warning not implemented */ virtual void SetGeometry(const itk::EventObject &geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometrySlice(const itk::EventObject &geometrySliceEvent); /** * \brief To connect multiple SliceNavigationController, we can * act as an observer ourselves: implemented interface */ virtual void SetGeometryTime(const itk::EventObject &geometryTimeEvent); /** \brief Positions the SNC according to the specified point */ void SelectSliceByPoint(const mitk::Point3D &point); /** \brief Returns the TimeGeometry created by the SNC. */ mitk::TimeGeometry *GetCreatedWorldGeometry(); /** \brief Returns the BaseGeometry of the currently selected time step. */ const mitk::BaseGeometry *GetCurrentGeometry3D(); /** \brief Returns the currently selected Plane in the current * BaseGeometry (if existent). */ const mitk::PlaneGeometry *GetCurrentPlaneGeometry(); /** \brief Sets the BaseRenderer associated with this SNC (if any). While * the BaseRenderer is not directly used by SNC, this is a convenience * method to enable BaseRenderer access via the SNC. */ void SetRenderer(BaseRenderer *renderer); /** \brief Gets the BaseRenderer associated with this SNC (if any). While * the BaseRenderer is not directly used by SNC, this is a convenience * method to enable BaseRenderer access via the SNC. Returns nullptr if no * BaseRenderer has been specified*/ BaseRenderer *GetRenderer() const; /** \brief Re-orients the slice stack. All slices will be oriented to the given normal vector. The given point (world coordinates) defines the selected slice. Careful: The resulting axis vectors are not clearly defined this way. If you want to define them clearly, use ReorientSlices (const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1). */ void ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &normal); /** \brief Re-orients the slice stack so that all planes are oriented according to the * given axis vectors. The given Point eventually defines selected slice. */ void ReorientSlices(const mitk::Point3D &point, const mitk::Vector3D &axisVec0, const mitk::Vector3D &axisVec1); void ExecuteOperation(Operation *operation) override; /** * \brief Feature option to lock planes during mouse interaction. * This option flag disables the mouse event which causes the center * cross to move near by. */ itkSetMacro(SliceLocked, bool); itkGetMacro(SliceLocked, bool); itkBooleanMacro(SliceLocked); /** * \brief Feature option to lock slice rotation. * * This option flag disables separately the rotation of a slice which is * implemented in mitkSliceRotator. */ itkSetMacro(SliceRotationLocked, bool); itkGetMacro(SliceRotationLocked, bool); itkBooleanMacro(SliceRotationLocked); /** * \brief Adjusts the numerical range of the slice stepper according to * the current geometry orientation of this SNC's SlicedGeometry. */ void AdjustSliceStepperRange(); /** \brief Convenience method that returns the time step currently selected by the controller.*/ TimeStepType GetSelectedTimeStep() const; /** \brief Convenience method that returns the time point that corresponds to the selected * time step. The conversion is done using the time geometry of the SliceNavigationController. * If the time geometry is not yet set, this function will always return 0.0.*/ TimePointType GetSelectedTimePoint() const; protected: SliceNavigationController(); ~SliceNavigationController() override; mitk::BaseGeometry::ConstPointer m_InputWorldGeometry3D; mitk::TimeGeometry::ConstPointer m_InputWorldTimeGeometry; mitk::TimeGeometry::Pointer m_CreatedWorldGeometry; ViewDirection m_ViewDirection; ViewDirection m_DefaultViewDirection; mitk::RenderingManager::Pointer m_RenderingManager; mitk::BaseRenderer *m_Renderer; itkSetMacro(Top, bool); itkGetMacro(Top, bool); itkBooleanMacro(Top); itkSetMacro(FrontSide, bool); itkGetMacro(FrontSide, bool); itkBooleanMacro(FrontSide); itkSetMacro(Rotated, bool); itkGetMacro(Rotated, bool); itkBooleanMacro(Rotated); bool m_Top; bool m_FrontSide; bool m_Rotated; bool m_BlockUpdate; bool m_SliceLocked; bool m_SliceRotationLocked; unsigned int m_OldPos; typedef std::map> ObserverTagsMapType; ObserverTagsMapType m_ReceiverToObserverTagsMap; }; } // namespace mitk #endif /* SLICENAVIGATIONCONTROLLER_H_HEADER_INCLUDED_C1C55A2F */ diff --git a/Modules/Core/include/mitkSlicesCoordinator.h b/Modules/Core/include/mitkSlicesCoordinator.h index 7397f1dda5..99c70fb09f 100644 --- a/Modules/Core/include/mitkSlicesCoordinator.h +++ b/Modules/Core/include/mitkSlicesCoordinator.h @@ -1,99 +1,99 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 SLICESCOORDINATOR_H_HEADER_INCLUDED_C1C55A2F #define SLICESCOORDINATOR_H_HEADER_INCLUDED_C1C55A2F #include #include #include #include namespace mitk { class SliceNavigationController; class Action; class StateEvent; #pragma GCC visibility push(default) - itkEventMacro(SliceRotationEvent, itk::AnyEvent); + itkEventMacroDeclaration(SliceRotationEvent, itk::AnyEvent); #pragma GCC visibility pop /** * \brief Coordinates a list of SliceNavigationControllers. * * Each SliceNavigationController can select one slice from a * TimeGeometry. This class (SlicesCoordinator) coordinates several * SliceNavigationControllers to facilitate e.g. rotation of slices. A new * class is needed, because for rotation one has to know an axis of rotation. * Such an axis is most easily determined from the "other slices", which are * not known by a SliceNavigationController. */ class MITKCORE_EXPORT SlicesCoordinator : public itk::Object { public: mitkClassMacroItkParent(SlicesCoordinator, itk::Object); itkFactorylessNewMacro(Self); typedef std::vector SNCVector; /** Add to list of managed slices. Check if CreatedWorldGeometry of SNC is * managable (i.e. there is basically only one planegeometry) */ void AddSliceController(SliceNavigationController *snc); /** Remove one controller from the internal list */ void RemoveSliceController(SliceNavigationController *snc); /* Reset all Slices to central slice, no rotation */ // void ResetAllSlices(); /** Set/Get whether planes should stay linked to each other (by fixing * their relative angle) */ itkSetMacro(LinkPlanes, bool); itkGetMacro(LinkPlanes, bool); itkBooleanMacro(LinkPlanes); /** \brief Resets the mouse cursor (if modified by the SlicesCoordinator) * to its original state. * * Should be used by subclasses and from external application instead * of using QmitkApplicationCursor directly to avoid conflicts. */ void ResetMouseCursor(); protected: /** \brief Default Constructor */ SlicesCoordinator(); /** clear list of controllers */ ~SlicesCoordinator() override; /** \brief Sets the specified mouse cursor. * * Use this in subclasses instead of using QmitkApplicationCursor directly. */ void SetMouseCursor(const char *xpm[], int hotspotX, int hotspotY); /** for implementation in subclasses */ virtual void OnSliceControllerAdded(SliceNavigationController *snc); /** for implementation in subclasses */ virtual void OnSliceControllerRemoved(SliceNavigationController *snc); SNCVector m_SliceNavigationControllers; bool m_LinkPlanes; bool m_MouseCursorSet; }; } // namespace #endif diff --git a/Modules/Core/include/mitkStandaloneDataStorage.h b/Modules/Core/include/mitkStandaloneDataStorage.h index 88e17af581..f026104ac9 100644 --- a/Modules/Core/include/mitkStandaloneDataStorage.h +++ b/Modules/Core/include/mitkStandaloneDataStorage.h @@ -1,129 +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 MITKSTANDALONEDATASTORAGE_H_HEADER_INCLUDED_ #define MITKSTANDALONEDATASTORAGE_H_HEADER_INCLUDED_ #include "itkVectorContainer.h" #include "mitkDataStorage.h" #include "mitkMessage.h" #include +#include namespace mitk { class NodePredicateBase; class DataNode; //##Documentation //## @brief Data management class that handles 'was created by' relations //## //## The StandaloneDataStorage provides data storage and management functionality. //## It handles a 'was created by' relation by associating each data object with a //## set of source objects that were used to create the new object was created from. //## Thus, nodes are stored in a noncyclical directed graph data structure. //## It is derived from mitk::DataStorage and implements its interface, //## including AddNodeEvent and RemoveNodeEvent. //## @ingroup StandaloneDataStorage class MITKCORE_EXPORT StandaloneDataStorage : public mitk::DataStorage { public: mitkClassMacro(StandaloneDataStorage, mitk::DataStorage); itkFactorylessNewMacro(Self); itkCloneMacro(Self); //##Documentation //## @brief Adds a DataNode containing a data object to its internal storage //## //## This Method adds a new data object to the StandaloneDataStorage. The new object is //## passed in the first parameter. The second parameter is a set //## of source objects, that were used to create this object. The new object will have //## a 'was created from' relation to its source objects. //## the addition of a new object will fire the notification mechanism. //## If the node parameter is nullptr or if the DataNode has already been added, //## an exception will be thrown. void Add(mitk::DataNode *node, const mitk::DataStorage::SetOfObjects *parents = nullptr) override; //##Documentation //## @brief Removes node from the StandaloneDataStorage //## void Remove(const mitk::DataNode *node) override; //##Documentation //## @brief Checks if a node exists in the StandaloneDataStorage //## bool Exists(const mitk::DataNode *node) const override; //##Documentation //## @brief returns a set of source objects for a given node that meet the given condition(s). //## SetOfObjects::ConstPointer GetSources(const mitk::DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectSources = true) const override; //##Documentation //## @brief returns a set of derived objects for a given node. //## //## GetDerivations() returns a set of objects that are derived from the DataNode node. //## This means, that node was used to create the returned objects. If the parameter //## onlyDirectDerivations is set to true (default value), only objects that directly have //## node as one of their source objects will be returned. Otherwise, objects that are //## derived from derivations of node are returned too. //## The derived objects can be filtered with a predicate object as described in the GetSubset() //## method by providing a predicate as the condition parameter. SetOfObjects::ConstPointer GetDerivations(const mitk::DataNode *node, const NodePredicateBase *condition = nullptr, bool onlyDirectDerivations = true) const override; //##Documentation //## @brief returns a set of all data objects that are stored in the data storage //## SetOfObjects::ConstPointer GetAll() const override; - /*ITK Mutex */ - mutable itk::SimpleFastMutexLock m_Mutex; + mutable std::mutex m_Mutex; protected: //##Documentation //## @brief noncyclical directed graph data structure to store the nodes with their relation typedef std::map AdjacencyList; StandaloneDataStorage(); ~StandaloneDataStorage() override; //##Documentation //## @brief convenience method to check if the object has been initialized (i.e. a data tree has been set) bool IsInitialized() const; //##Documentation //## @brief Traverses the Relation graph and extracts a list of related elements (e.g. Sources or Derivations) SetOfObjects::ConstPointer GetRelations(const mitk::DataNode *node, const AdjacencyList &relation, const NodePredicateBase *condition = nullptr, bool onlyDirectlyRelated = true) const; //##Documentation //## @brief deletes all references to a node in a given relation (used in Remove() and TreeListener) void RemoveFromRelation(const mitk::DataNode *node, AdjacencyList &relation); //##Documentation //## @brief Prints the contents of the StandaloneDataStorage to os. Do not call directly, call ->Print() instead void PrintSelf(std::ostream &os, itk::Indent indent) const override; //##Documentation //## @brief Nodes and their relation are stored in m_SourceNodes AdjacencyList m_SourceNodes; //##Documentation //## @brief Nodes are stored in reverse relation for easier traversal in the opposite direction of the relation AdjacencyList m_DerivedNodes; }; } // namespace mitk #endif /* MITKSTANDALONEDATASTORAGE_H_HEADER_INCLUDED_ */ diff --git a/Modules/Core/include/mitkUtf8Util.h b/Modules/Core/include/mitkUtf8Util.h new file mode 100644 index 0000000000..fd14bead54 --- /dev/null +++ b/Modules/Core/include/mitkUtf8Util.h @@ -0,0 +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 mitkUtf8Util_h +#define mitkUtf8Util_h + +#include +#include + +namespace mitk +{ + namespace Utf8Util + { + /** + * @brief Convert a string encoded with the current code page to an UTF-8 encoded string (Windows) + * + * The conversion happens on Windows only. On all other platforms, the input string + * is returned unmodified as it is assumed to be UTF-8 encoded already. + * + * If the conversion fails, a warning is printed and the input string is returned + * instead. This matches the behavior before this method was introduced. + */ + MITKCORE_EXPORT std::string Local8BitToUtf8(const std::string& local8BitStr); + + /** + * @brief Convert a UTF-8 encoded string to a string encoded with the current code page (Windows) + * + * The conversion happens on Windows only. On all other platforms, the input string + * is returned unmodified as strings are assumed to be always UTF-8 encoded by default. + * + * If the conversion fails, a warning is printed and the input string is returned + * instead. This matches the behavior before this method was introduced. + */ + MITKCORE_EXPORT std::string Utf8ToLocal8Bit(const std::string& utf8Str); + } +} + +#endif diff --git a/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp b/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp index e448408faa..d6ba0822f6 100644 --- a/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp +++ b/Modules/Core/src/Algorithms/mitkExtractSliceFilter2.cpp @@ -1,281 +1,281 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 struct mitk::ExtractSliceFilter2::Impl { Impl(); ~Impl(); PlaneGeometry::Pointer OutputGeometry; mitk::ExtractSliceFilter2::Interpolator Interpolator; itk::Object::Pointer InterpolateImageFunction; }; mitk::ExtractSliceFilter2::Impl::Impl() : Interpolator(NearestNeighbor) { } mitk::ExtractSliceFilter2::Impl::~Impl() { } namespace { template void CreateInterpolateImageFunction(const TInputImage* inputImage, mitk::ExtractSliceFilter2::Interpolator interpolator, itk::Object::Pointer& result) { typename itk::InterpolateImageFunction::Pointer interpolateImageFunction; switch (interpolator) { case mitk::ExtractSliceFilter2::NearestNeighbor: interpolateImageFunction = itk::NearestNeighborInterpolateImageFunction::New().GetPointer(); break; case mitk::ExtractSliceFilter2::Linear: interpolateImageFunction = itk::LinearInterpolateImageFunction::New().GetPointer(); break; case mitk::ExtractSliceFilter2::Cubic: { auto bSplineInterpolateImageFunction = itk::BSplineInterpolateImageFunction::New(); bSplineInterpolateImageFunction->SetSplineOrder(2); interpolateImageFunction = bSplineInterpolateImageFunction.GetPointer(); break; } default: mitkThrow() << "Interplator is unknown."; } interpolateImageFunction->SetInputImage(inputImage); result = interpolateImageFunction.GetPointer(); } template void GenerateData(const itk::Image* inputImage, mitk::Image* outputImage, const mitk::ExtractSliceFilter2::OutputImageRegionType& outputRegion, itk::Object* interpolateImageFunction) { typedef itk::Image TInputImage; typedef itk::InterpolateImageFunction TInterpolateImageFunction; auto outputGeometry = outputImage->GetSlicedGeometry()->GetPlaneGeometry(0); auto interpolator = static_cast(interpolateImageFunction); auto origin = outputGeometry->GetOrigin(); auto spacing = outputGeometry->GetSpacing(); auto xDirection = outputGeometry->GetAxisVector(0); auto yDirection = outputGeometry->GetAxisVector(1); xDirection.Normalize(); yDirection.Normalize(); auto spacingAlongXDirection = xDirection * spacing[0]; auto spacingAlongYDirection = yDirection * spacing[1]; const std::size_t pixelSize = outputImage->GetPixelType().GetSize(); const std::size_t width = outputGeometry->GetExtent(0); const std::size_t xBegin = outputRegion.GetIndex(0); const std::size_t yBegin = outputRegion.GetIndex(1); const std::size_t xEnd = xBegin + outputRegion.GetSize(0); const std::size_t yEnd = yBegin + outputRegion.GetSize(1); mitk::ImageWriteAccessor writeAccess(outputImage, nullptr, mitk::ImageAccessorBase::IgnoreLock); auto data = static_cast(writeAccess.GetData()); const TPixel backgroundPixel = std::numeric_limits::lowest(); TPixel pixel; itk::ContinuousIndex index; mitk::Point3D yPoint; mitk::Point3D point; for (std::size_t y = yBegin; y < yEnd; ++y) { yPoint = origin + spacingAlongYDirection * y; for (std::size_t x = xBegin; x < xEnd; ++x) { point = yPoint + spacingAlongXDirection * x; if (inputImage->TransformPhysicalPointToContinuousIndex(point, index)) { pixel = interpolator->EvaluateAtContinuousIndex(index); memcpy(static_cast(data + pixelSize * (width * y + x)), static_cast(&pixel), pixelSize); } else { memcpy(static_cast(data + pixelSize * (width * y + x)), static_cast(&backgroundPixel), pixelSize); } } } } void VerifyInputImage(const mitk::Image* inputImage) { auto dimension = inputImage->GetDimension(); if (3 != dimension) mitkThrow() << "Input images with " << dimension << " dimensions are not supported."; if (!inputImage->IsInitialized()) mitkThrow() << "Input image is not initialized."; if (!inputImage->IsVolumeSet()) mitkThrow() << "Input image volume is not set."; auto geometry = inputImage->GetGeometry(); if (nullptr == geometry || !geometry->IsValid()) mitkThrow() << "Input image has invalid geometry."; if (!geometry->GetImageGeometry()) mitkThrow() << "Geometry of input image is not an image geometry."; } void VerifyOutputGeometry(const mitk::PlaneGeometry* outputGeometry) { if (nullptr == outputGeometry) mitkThrow() << "Output geometry is not set."; if (!outputGeometry->GetImageGeometry()) mitkThrow() << "Output geometry is not an image geometry."; } } mitk::ExtractSliceFilter2::ExtractSliceFilter2() : m_Impl(new Impl) { } mitk::ExtractSliceFilter2::~ExtractSliceFilter2() { delete m_Impl; } void mitk::ExtractSliceFilter2::AllocateOutputs() { const auto* inputImage = this->GetInput(); const auto* outputGeometry = this->GetOutputGeometry(); auto outputImage = this->GetOutput(); auto pixelType = inputImage->GetPixelType(); outputImage->Initialize(pixelType, 1, *outputGeometry); auto data = new char[static_cast(pixelType.GetSize() * outputGeometry->GetExtent(0) * outputGeometry->GetExtent(1))]; try { if (!outputImage->SetImportVolume(data, 0, 0, mitk::Image::ReferenceMemory)) throw; } catch (...) { delete[] data; } } /*void mitk::ExtractSliceFilter2::BeforeThreadedGenerateData() { if (nullptr != m_Impl->InterpolateImageFunction && this->GetInput()->GetMTime() < this->GetMTime()) return; const auto* inputImage = this->GetInput(); AccessFixedDimensionByItk_2(inputImage, CreateInterpolateImageFunction, 3, this->GetInterpolator(), m_Impl->InterpolateImageFunction); } void mitk::ExtractSliceFilter2::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType) { const auto* inputImage = this->GetInput(); AccessFixedDimensionByItk_3(inputImage, ::GenerateData, 3, this->GetOutput(), outputRegionForThread, m_Impl->InterpolateImageFunction); }*/ void mitk::ExtractSliceFilter2::GenerateData() { if (nullptr != m_Impl->InterpolateImageFunction && this->GetInput()->GetMTime() < this->GetMTime()) return; const auto* inputImage = this->GetInput(); AccessFixedDimensionByItk_2(inputImage, CreateInterpolateImageFunction, 3, this->GetInterpolator(), m_Impl->InterpolateImageFunction); this->AllocateOutputs(); auto outputRegion = this->GetOutput()->GetLargestPossibleRegion(); AccessFixedDimensionByItk_3(inputImage, ::GenerateData, 3, this->GetOutput(), outputRegion, m_Impl->InterpolateImageFunction); } void mitk::ExtractSliceFilter2::SetInput(const InputImageType* image) { if (this->GetInput() == image) return; Superclass::SetInput(image); m_Impl->InterpolateImageFunction = nullptr; } void mitk::ExtractSliceFilter2::SetInput(unsigned int index, const InputImageType* image) { if (0 != index) mitkThrow() << "Input index " << index << " is invalid."; this->SetInput(image); } const mitk::PlaneGeometry* mitk::ExtractSliceFilter2::GetOutputGeometry() const { return m_Impl->OutputGeometry; } void mitk::ExtractSliceFilter2::SetOutputGeometry(PlaneGeometry::Pointer outputGeometry) { if (m_Impl->OutputGeometry != outputGeometry) { m_Impl->OutputGeometry = outputGeometry; this->Modified(); } } mitk::ExtractSliceFilter2::Interpolator mitk::ExtractSliceFilter2::GetInterpolator() const { return m_Impl->Interpolator; } void mitk::ExtractSliceFilter2::SetInterpolator(Interpolator interpolator) { if (m_Impl->Interpolator != interpolator) { m_Impl->Interpolator = interpolator; m_Impl->InterpolateImageFunction = nullptr; this->Modified(); } } -void mitk::ExtractSliceFilter2::VerifyInputInformation() +void mitk::ExtractSliceFilter2::VerifyInputInformation() const { Superclass::VerifyInputInformation(); VerifyInputImage(this->GetInput()); VerifyOutputGeometry(this->GetOutputGeometry()); } diff --git a/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp b/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp index 3907e3b40b..6802fb3c62 100644 --- a/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp +++ b/Modules/Core/src/Algorithms/mitkHistogramGenerator.cpp @@ -1,125 +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. ============================================================================*/ #if (_MSC_VER == 1200) #include #endif #include "mitkHistogramGenerator.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" // // The new ITK Statistics framework has // a class with the same functionality as // MITKScalarImageToHistogramGenerator.h, but // no longer has the classis the MITK class depends on. #if !defined(ITK_USE_REVIEW_STATISTICS) #include "itkMITKScalarImageToHistogramGenerator.h" #else #include "itkScalarImageToHistogramGenerator.h" #endif mitk::HistogramGenerator::HistogramGenerator() : m_Image(nullptr), m_Size(256), m_Histogram(nullptr) { } mitk::HistogramGenerator::~HistogramGenerator() { } template void InternalCompute(itk::Image *itkImage, const mitk::HistogramGenerator *mitkHistoGenerator, mitk::HistogramGenerator::HistogramType::ConstPointer &histogram) { #if !defined(ITK_USE_REVIEW_STATISTICS) typedef itk::Statistics::MITKScalarImageToHistogramGenerator, double> HistogramGeneratorType; #else typedef itk::Statistics::ScalarImageToHistogramGenerator> HistogramGeneratorType; #endif typename HistogramGeneratorType::Pointer histogramGenerator = HistogramGeneratorType::New(); histogramGenerator->SetInput(itkImage); histogramGenerator->SetNumberOfBins(mitkHistoGenerator->GetSize()); // histogramGenerator->SetMarginalScale( 10.0 ); histogramGenerator->Compute(); histogram = histogramGenerator->GetOutput(); } void mitk::HistogramGenerator::ComputeHistogram() { if ((m_Histogram.IsNull()) || (m_Histogram->GetMTime() < m_Image->GetMTime())) { const_cast(m_Image.GetPointer())->SetRequestedRegionToLargestPossibleRegion(); //@todo without this, // Image::GetScalarMin // does not work for // dim==3 (including // sliceselector!) const_cast(m_Image.GetPointer())->Update(); mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(m_Image); timeSelector->SetTimeNr(0); timeSelector->UpdateLargestPossibleRegion(); AccessByItk_n(timeSelector->GetOutput(), InternalCompute, (this, m_Histogram)); } // debug code /* MITK_INFO << "Histogram modfied 1" << m_Histogram->GetMTime() << std::endl; m_Histogram->Modified(); MITK_INFO << "Histogram modfied 2" << m_Histogram->GetMTime() << std::endl; MITK_INFO << "Image modfied" << m_Image->GetMTime() << std::endl; const unsigned int histogramSize = m_Histogram->Size(); MITK_INFO << "Histogram size " << histogramSize << std::endl; HistogramType::ConstIterator itr = GetHistogram()->Begin(); HistogramType::ConstIterator end = GetHistogram()->End(); int bin = 0; while( itr != end ) { MITK_INFO << "bin = " << GetHistogram()->GetBinMin(0,bin) << "--" << GetHistogram()->GetBinMax(0,bin) << " frequency = "; MITK_INFO << itr.GetFrequency() << std::endl; ++itr; ++bin; } */ } float mitk::HistogramGenerator::GetMaximumFrequency() const { return CalculateMaximumFrequency(this->m_Histogram); -}; +} float mitk::HistogramGenerator::CalculateMaximumFrequency(const HistogramType *histogram) { HistogramType::ConstIterator itr = histogram->Begin(); HistogramType::ConstIterator end = histogram->End(); float maxFreq = 0; while (itr != end) { - maxFreq = vnl_math_max(maxFreq, - // get rid of ambiguity with type signature - // for vnl_math_max - static_cast(itr.GetFrequency())); + maxFreq = std::max(maxFreq, static_cast(itr.GetFrequency())); ++itr; } return maxFreq; -}; +} diff --git a/Modules/Core/src/Algorithms/mitkImageSource.cpp b/Modules/Core/src/Algorithms/mitkImageSource.cpp index 8ebc72332d..7a4dc03182 100644 --- a/Modules/Core/src/Algorithms/mitkImageSource.cpp +++ b/Modules/Core/src/Algorithms/mitkImageSource.cpp @@ -1,199 +1,201 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkImageSource.h" #include "mitkImageVtkReadAccessor.h" #include "mitkImageVtkWriteAccessor.h" +#include + mitk::ImageSource::ImageSource() { // Create the output. We use static_cast<> here because we know the default // output must be of type TOutputImage OutputImageType::Pointer output = static_cast(this->MakeOutput(0).GetPointer()); Superclass::SetNumberOfRequiredOutputs(1); Superclass::SetNthOutput(0, output.GetPointer()); } itk::DataObject::Pointer mitk::ImageSource::MakeOutput(DataObjectPointerArraySizeType /*idx*/) { return static_cast(mitk::Image::New().GetPointer()); } itk::DataObject::Pointer mitk::ImageSource::MakeOutput(const DataObjectIdentifierType &name) { itkDebugMacro("MakeOutput(" << name << ")"); if (this->IsIndexedOutputName(name)) { return this->MakeOutput(this->MakeIndexFromOutputName(name)); } return static_cast(mitk::Image::New().GetPointer()); } //---------------------------------------------------------------------------- unsigned int mitk::ImageSource::SplitRequestedRegion(unsigned int i, unsigned int num, OutputImageRegionType &splitRegion) { // Get the output pointer OutputImageType *outputPtr = this->GetOutput(); const SlicedData::SizeType &requestedRegionSize = outputPtr->GetRequestedRegion().GetSize(); int splitAxis; SlicedData::IndexType splitIndex; SlicedData::SizeType splitSize; // Initialize the splitRegion to the output requested region splitRegion = outputPtr->GetRequestedRegion(); splitIndex = splitRegion.GetIndex(); splitSize = splitRegion.GetSize(); // split on the outermost dimension available splitAxis = outputPtr->GetDimension() - 1; while (requestedRegionSize[splitAxis] == 1) { --splitAxis; if (splitAxis < 0) { // cannot split itkDebugMacro(" Cannot Split"); return 1; } } // determine the actual number of pieces that will be generated SlicedData::SizeType::SizeValueType range = requestedRegionSize[splitAxis]; auto valuesPerThread = itk::Math::Ceil(range / (double)num); unsigned int maxThreadIdUsed = itk::Math::Ceil(range / (double)valuesPerThread) - 1; // Split the region if (i < maxThreadIdUsed) { splitIndex[splitAxis] += i * valuesPerThread; splitSize[splitAxis] = valuesPerThread; } if (i == maxThreadIdUsed) { splitIndex[splitAxis] += i * valuesPerThread; // last thread needs to process the "rest" dimension being split splitSize[splitAxis] = splitSize[splitAxis] - i * valuesPerThread; } // set the split region ivars splitRegion.SetIndex(splitIndex); splitRegion.SetSize(splitSize); itkDebugMacro(" Split Piece: " << splitRegion); return maxThreadIdUsed + 1; } //---------------------------------------------------------------------------- void mitk::ImageSource::AllocateOutputs() { OutputImagePointer outputPtr; // Allocate the output memory for (unsigned int i = 0; i < this->GetNumberOfOutputs(); i++) { outputPtr = this->GetOutput(i); // outputPtr->SetBufferedRegion(outputPtr->GetRequestedRegion()); @FIXME??? // outputPtr->Allocate(); @FIXME??? } } //---------------------------------------------------------------------------- void mitk::ImageSource::GenerateData() { // Call a method that can be overriden by a subclass to allocate // memory for the filter's outputs this->AllocateOutputs(); // Call a method that can be overridden by a subclass to perform // some calculations prior to splitting the main computations into // separate threads this->BeforeThreadedGenerateData(); // Set up the multithreaded processing ThreadStruct str; str.Filter = this; - this->GetMultiThreader()->SetNumberOfThreads(this->GetNumberOfThreads()); + this->GetMultiThreader()->SetNumberOfWorkUnits(this->GetNumberOfWorkUnits()); this->GetMultiThreader()->SetSingleMethod(this->ThreaderCallback, &str); // multithread the execution this->GetMultiThreader()->SingleMethodExecute(); // Call a method that can be overridden by a subclass to perform // some calculations after all the threads have completed this->AfterThreadedGenerateData(); } //---------------------------------------------------------------------------- // The execute method created by the subclass. void mitk::ImageSource::ThreadedGenerateData(const OutputImageRegionType &, itk::ThreadIdType) { itkExceptionMacro("subclass should override this method!!!"); } // Callback routine used by the threading library. This routine just calls // the ThreadedGenerateData method after setting the correct region for this // thread. -ITK_THREAD_RETURN_TYPE mitk::ImageSource::ThreaderCallback(void *arg) +itk::ITK_THREAD_RETURN_TYPE mitk::ImageSource::ThreaderCallback(void *arg) { ThreadStruct *str; itk::ThreadIdType total, threadId, threadCount; - threadId = ((itk::MultiThreader::ThreadInfoStruct *)(arg))->ThreadID; - threadCount = ((itk::MultiThreader::ThreadInfoStruct *)(arg))->NumberOfThreads; + threadId = ((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->WorkUnitID; + threadCount = ((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->NumberOfWorkUnits; - str = (ThreadStruct *)(((itk::MultiThreader::ThreadInfoStruct *)(arg))->UserData); + str = (ThreadStruct *)(((itk::MultiThreaderBase::WorkUnitInfo *)(arg))->UserData); // execute the actual method with appropriate output region // first find out how many pieces extent can be split into. SlicedData::RegionType splitRegion; total = str->Filter->SplitRequestedRegion(threadId, threadCount, splitRegion); if (threadId < total) { str->Filter->ThreadedGenerateData(splitRegion, threadId); } // else // { // otherwise don't use this thread. Sometimes the threads dont // break up very well and it is just as efficient to leave a // few threads idle. // } - return ITK_THREAD_RETURN_VALUE; + return itk::ITK_THREAD_RETURN_DEFAULT_VALUE; } void mitk::ImageSource::PrepareOutputs() { Superclass::PrepareOutputs(); } vtkImageData *mitk::ImageSource::GetVtkImageData() { Update(); return GetOutput()->GetVtkImageData(); } const vtkImageData *mitk::ImageSource::GetVtkImageData() const { return GetOutput()->GetVtkImageData(); } mitkBaseDataSourceGetOutputDefinitions(mitk::ImageSource) diff --git a/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp b/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp index 5de2fdba82..130adf3ce4 100644 --- a/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp @@ -1,161 +1,161 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 includes #include "mitkMultiComponentImageDataComparisonFilter.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImageReadAccessor.h" // other includes // #include namespace mitk { MultiComponentImageDataComparisonFilter::MultiComponentImageDataComparisonFilter() : ImageToImageFilter(), m_Tolerance(0.0f), m_CompareResult(false), m_CompareDetails(nullptr) { this->SetNumberOfRequiredInputs(2); } MultiComponentImageDataComparisonFilter::~MultiComponentImageDataComparisonFilter() {} bool MultiComponentImageDataComparisonFilter::GetResult(double threshold) { if (!m_CompareResult) { return false; } if (m_CompareDetails->m_PixelsWithDifference > threshold) { return false; } return true; } void MultiComponentImageDataComparisonFilter::SetValidImage(const Image *_arg) { this->SetInput(0, _arg); } void MultiComponentImageDataComparisonFilter::SetTestImage(const Image *_arg) { this->SetInput(1, _arg); } const Image *MultiComponentImageDataComparisonFilter::GetValidImage() { return this->GetInput(0); } const Image *MultiComponentImageDataComparisonFilter::GetTestImage() { return this->GetInput(1); } void MultiComponentImageDataComparisonFilter::SetCompareFilterResult(CompareFilterResults *results) { m_CompareDetails = results; } CompareFilterResults *MultiComponentImageDataComparisonFilter::GetCompareFilterResult() { return m_CompareDetails; } void MultiComponentImageDataComparisonFilter::GenerateData() { // check inputs const Image *testInput = this->GetTestImage(); const Image *validInput = this->GetValidImage(); // Generally this filter is part of the mitk::Image::Equal() method and only checks the equality of the image data // so no further image type comparison is performed! // CAVE: If the images differ in a parameter other then the image data, the filter may fail!! PixelType type = validInput->GetPixelType(); - if (type.GetComponentType() == itk::ImageIOBase::CHAR) + if (type.GetComponentType() == itk::IOComponentEnum::CHAR) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::UCHAR) + else if (type.GetComponentType() == itk::IOComponentEnum::UCHAR) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::INT) + else if (type.GetComponentType() == itk::IOComponentEnum::INT) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::UINT) + else if (type.GetComponentType() == itk::IOComponentEnum::UINT) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::SHORT) + else if (type.GetComponentType() == itk::IOComponentEnum::SHORT) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::USHORT) + else if (type.GetComponentType() == itk::IOComponentEnum::USHORT) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::LONG) + else if (type.GetComponentType() == itk::IOComponentEnum::LONG) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::ULONG) + else if (type.GetComponentType() == itk::IOComponentEnum::ULONG) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::FLOAT) + else if (type.GetComponentType() == itk::IOComponentEnum::FLOAT) { CompareMultiComponentImage(testInput, validInput); } - else if (type.GetComponentType() == itk::ImageIOBase::DOUBLE) + else if (type.GetComponentType() == itk::IOComponentEnum::DOUBLE) { CompareMultiComponentImage(testInput, validInput); } else { mitkThrow() << "Pixel component type not supported!"; } } template void mitk::MultiComponentImageDataComparisonFilter::CompareMultiComponentImage(const Image *testImage, const Image *validImage) { unsigned int noOfTimes = validImage->GetDimension(3); unsigned int noOfPixels = validImage->GetDimension(0) * validImage->GetDimension(1) * validImage->GetDimension(2); unsigned int noOfComponents = validImage->GetPixelType().GetNumberOfComponents(); for (unsigned int t = 0; t < noOfTimes; ++t) { ImageReadAccessor readAccTImage(testImage, testImage->GetVolumeData(t)); ImageReadAccessor readAccVImage(validImage, validImage->GetVolumeData(t)); for (unsigned int p = 0; p < noOfPixels * noOfComponents; ++p) { TPixel vDataItem = static_cast(const_cast(readAccVImage.GetData()))[p]; TPixel tDataItem = static_cast(const_cast(readAccTImage.GetData()))[p]; if (std::abs(static_cast(tDataItem - vDataItem)) > m_Tolerance) { ++m_CompareDetails->m_PixelsWithDifference; m_CompareDetails->m_MaximumDifference = std::max(m_CompareDetails->m_MaximumDifference, std::abs(static_cast(tDataItem - vDataItem))); double min = std::min(m_CompareDetails->m_MinimumDifference, std::abs(static_cast(tDataItem - vDataItem))); if (min != 0.0f) // a difference of zero is not a difference! m_CompareDetails->m_MinimumDifference = min; m_CompareDetails->m_TotalDifference += std::abs(static_cast(tDataItem - vDataItem)); } } } if (m_CompareDetails->m_PixelsWithDifference > 0) { m_CompareDetails->m_MeanDifference = m_CompareDetails->m_TotalDifference / m_CompareDetails->m_PixelsWithDifference; m_CompareResult = false; } else { m_CompareResult = true; } m_CompareDetails->m_FilterCompleted = true; } } // end namespace mitk diff --git a/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp b/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp index 2ebeffa059..86fe47f153 100644 --- a/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkRGBToRGBACastImageFilter.cpp @@ -1,211 +1,211 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkRGBToRGBACastImageFilter.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include "mitkImageToItk.h" #include "mitkProperties.h" #include #include #include #include mitk::RGBToRGBACastImageFilter::RGBToRGBACastImageFilter() { this->SetNumberOfIndexedInputs(1); this->SetNumberOfRequiredInputs(1); m_InputTimeSelector = mitk::ImageTimeSelector::New(); m_OutputTimeSelector = mitk::ImageTimeSelector::New(); } mitk::RGBToRGBACastImageFilter::~RGBToRGBACastImageFilter() { } bool mitk::RGBToRGBACastImageFilter::IsRGBImage(const mitk::Image *image) { const mitk::PixelType &inputPixelType = image->GetPixelType(); - if ((inputPixelType.GetPixelType() == itk::ImageIOBase::RGB) && - ((inputPixelType.GetComponentType() == itk::ImageIOBase::UCHAR) || - (inputPixelType.GetComponentType() == itk::ImageIOBase::USHORT) || - (inputPixelType.GetComponentType() == itk::ImageIOBase::FLOAT) || - (inputPixelType.GetComponentType() == itk::ImageIOBase::DOUBLE))) + if ((inputPixelType.GetPixelType() == itk::IOPixelEnum::RGB) && + ((inputPixelType.GetComponentType() == itk::IOComponentEnum::UCHAR) || + (inputPixelType.GetComponentType() == itk::IOComponentEnum::USHORT) || + (inputPixelType.GetComponentType() == itk::IOComponentEnum::FLOAT) || + (inputPixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE))) { return true; } return false; } void mitk::RGBToRGBACastImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); mitk::Image *output = this->GetOutput(); mitk::Image *input = this->GetInput(); if (!output->IsInitialized()) { return; } input->SetRequestedRegionToLargestPossibleRegion(); // GenerateTimeInInputRegion(output, input); } void mitk::RGBToRGBACastImageFilter::GenerateOutputInformation() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (this->GetMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; itkDebugMacro(<< "GenerateOutputInformation()"); // Initialize RGBA output with same pixel type as input image const mitk::PixelType &inputPixelType = input->GetPixelType(); typedef itk::Image UCRGBItkImageType; typedef itk::Image USRGBItkImageType; typedef itk::Image FloatCRGBItkImageType; typedef itk::Image DoubleRGBItkImageType; if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } else if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } else if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } else if (inputPixelType == mitk::MakePixelType()) { const mitk::PixelType refPtype = MakePixelType(); output->Initialize(refPtype, *input->GetTimeGeometry()); } output->SetPropertyList(input->GetPropertyList()->Clone()); m_TimeOfHeaderInitialization.Modified(); } void mitk::RGBToRGBACastImageFilter::GenerateData() { mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if (!output->IsInitialized()) { return; } m_InputTimeSelector->SetInput(input); m_OutputTimeSelector->SetInput(this->GetOutput()); mitk::Image::RegionType outputRegion = output->GetRequestedRegion(); const mitk::TimeGeometry *outputTimeGeometry = output->GetTimeGeometry(); const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); TimePointType timeInMS; int timestep = 0; int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); int t; for (t = tstart; t < tmax; ++t) { timeInMS = outputTimeGeometry->TimeStepToTimePoint(t); timestep = inputTimeGeometry->TimePointToTimeStep(timeInMS); m_InputTimeSelector->SetTimeNr(timestep); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr(t); m_OutputTimeSelector->UpdateLargestPossibleRegion(); mitk::Image *image = m_InputTimeSelector->GetOutput(); const mitk::PixelType &pixelType = image->GetPixelType(); // Check if the pixel type is supported if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (UCRGBPixelType), this, 255); } else if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (USRGBPixelType), this, 65535); } else if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (FloatRGBPixelType), this, 1.0); } else if (pixelType == MakePixelType>()) { AccessFixedPixelTypeByItk_2(image, InternalCast, (DoubleRGBPixelType), this, 1.0); } else { // Otherwise, write warning and graft input to output // ...TBD... } } m_TimeOfHeaderInitialization.Modified(); } template void mitk::RGBToRGBACastImageFilter::InternalCast(itk::Image *inputItkImage, mitk::RGBToRGBACastImageFilter *addComponentFilter, typename TPixel::ComponentType defaultAlpha) { typedef TPixel InputPixelType; typedef itk::RGBAPixel OutputPixelType; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::ImageRegionConstIterator InputImageIteratorType; typedef itk::ImageRegionIteratorWithIndex OutputImageIteratorType; typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); outputimagetoitk->SetInput(addComponentFilter->m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename OutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); // create the iterators typename InputImageType::RegionType inputRegionOfInterest = inputItkImage->GetLargestPossibleRegion(); InputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); OutputImageIteratorType outputIt(outputItkImage, inputRegionOfInterest); for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt) { typename InputPixelType::Iterator pixelInputIt = inputIt.Get().Begin(); typename OutputPixelType::Iterator pixelOutputIt = outputIt.Get().Begin(); *pixelOutputIt++ = *pixelInputIt++; *pixelOutputIt++ = *pixelInputIt++; *pixelOutputIt++ = *pixelInputIt++; *pixelOutputIt = defaultAlpha; } } diff --git a/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp b/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp index 08d7f3dae4..7f69c6ee80 100644 --- a/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp +++ b/Modules/Core/src/Controllers/mitkLimitedLinearUndo.cpp @@ -1,236 +1,247 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkLimitedLinearUndo.h" #include +namespace mitk +{ + itkEventMacroDefinition(UndoStackEvent, itk::ModifiedEvent); + itkEventMacroDefinition(UndoEmptyEvent, UndoStackEvent); + itkEventMacroDefinition(RedoEmptyEvent, UndoStackEvent); + itkEventMacroDefinition(UndoNotEmptyEvent, UndoStackEvent); + itkEventMacroDefinition(RedoNotEmptyEvent, UndoStackEvent); + itkEventMacroDefinition(UndoFullEvent, UndoStackEvent); + itkEventMacroDefinition(RedoFullEvent, UndoStackEvent); +} + mitk::LimitedLinearUndo::LimitedLinearUndo() : m_UndoLimit(0) { // nothing to do } mitk::LimitedLinearUndo::~LimitedLinearUndo() { // delete undo and redo list this->ClearList(&m_UndoList); this->ClearList(&m_RedoList); } void mitk::LimitedLinearUndo::ClearList(UndoContainer *list) { while (!list->empty()) { UndoStackItem *item = list->back(); list->pop_back(); delete item; } } bool mitk::LimitedLinearUndo::SetOperationEvent(UndoStackItem *stackItem) { auto *operationEvent = dynamic_cast(stackItem); if (!operationEvent) return false; // clear the redolist, if a new operation is saved if (!m_RedoList.empty()) { this->ClearList(&m_RedoList); InvokeEvent(RedoEmptyEvent()); } if (0 != m_UndoLimit && m_UndoList.size() == m_UndoLimit) { auto item = m_UndoList.front(); m_UndoList.pop_front(); delete item; } m_UndoList.push_back(operationEvent); InvokeEvent(UndoNotEmptyEvent()); return true; } bool mitk::LimitedLinearUndo::Undo(bool fine) { if (fine) { // undo one object event ID return Undo(); } else { // undo one group event ID int oeid = FirstObjectEventIdOfCurrentGroup( m_UndoList); // get the Object Event ID of the first item with a differnt Group ID (as seen from the end of stack) return Undo(oeid); } } bool mitk::LimitedLinearUndo::Undo() { if (m_UndoList.empty()) return false; int undoObjectEventId = m_UndoList.back()->GetObjectEventId(); return Undo(undoObjectEventId); } bool mitk::LimitedLinearUndo::Undo(int oeid) { if (m_UndoList.empty()) return false; bool rc = true; do { m_UndoList.back()->ReverseAndExecute(); m_RedoList.push_back(m_UndoList.back()); // move to redo stack m_UndoList.pop_back(); InvokeEvent(RedoNotEmptyEvent()); if (m_UndoList.empty()) { InvokeEvent(UndoEmptyEvent()); rc = false; break; } } while (m_UndoList.back()->GetObjectEventId() >= oeid); // Update. Check Rendering Mechanism where to request updates mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return rc; } bool mitk::LimitedLinearUndo::Redo(bool) { return Redo(); } bool mitk::LimitedLinearUndo::Redo() { if (m_RedoList.empty()) return false; int redoObjectEventId = m_RedoList.back()->GetObjectEventId(); return Redo(redoObjectEventId); } bool mitk::LimitedLinearUndo::Redo(int oeid) { if (m_RedoList.empty()) return false; do { m_RedoList.back()->ReverseAndExecute(); m_UndoList.push_back(m_RedoList.back()); m_RedoList.pop_back(); InvokeEvent(UndoNotEmptyEvent()); if (m_RedoList.empty()) { InvokeEvent(RedoEmptyEvent()); break; } } while (m_RedoList.back()->GetObjectEventId() <= oeid); // Update. This should belong into the ExecuteOperation() of OperationActors, but it seems not to be used everywhere mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } void mitk::LimitedLinearUndo::Clear() { this->ClearList(&m_UndoList); InvokeEvent(UndoEmptyEvent()); this->ClearList(&m_RedoList); InvokeEvent(RedoEmptyEvent()); } void mitk::LimitedLinearUndo::ClearRedoList() { this->ClearList(&m_RedoList); InvokeEvent(RedoEmptyEvent()); } bool mitk::LimitedLinearUndo::RedoListEmpty() { return m_RedoList.empty(); } std::size_t mitk::LimitedLinearUndo::GetUndoLimit() const { return m_UndoLimit; } void mitk::LimitedLinearUndo::SetUndoLimit(std::size_t undoLimit) { if (undoLimit != m_UndoLimit) { if (m_UndoList.size() > undoLimit) { m_UndoList.erase(m_UndoList.begin(), m_UndoList.end() - undoLimit); } m_UndoLimit = undoLimit; } } int mitk::LimitedLinearUndo::GetLastObjectEventIdInList() { return m_UndoList.back()->GetObjectEventId(); } int mitk::LimitedLinearUndo::GetLastGroupEventIdInList() { return m_UndoList.back()->GetGroupEventId(); } mitk::OperationEvent *mitk::LimitedLinearUndo::GetLastOfType(OperationActor *destination, OperationType opType) { // When/where is this function needed? In CoordinateSupplier... for (auto iter = m_UndoList.rbegin(); iter != m_UndoList.rend(); ++iter) { auto *opEvent = dynamic_cast(*iter); if (!opEvent) continue; if (opEvent->GetOperation() != nullptr && opEvent->GetOperation()->GetOperationType() == opType && opEvent->IsValid() && opEvent->GetDestination() == destination) return opEvent; } return nullptr; } int mitk::LimitedLinearUndo::FirstObjectEventIdOfCurrentGroup(mitk::LimitedLinearUndo::UndoContainer &stack) { int currentGroupEventId = stack.back()->GetGroupEventId(); int firstObjectEventId = -1; for (auto iter = stack.rbegin(); iter != stack.rend(); ++iter) { if ((*iter)->GetGroupEventId() == currentGroupEventId) { firstObjectEventId = (*iter)->GetObjectEventId(); } else break; } return firstObjectEventId; } diff --git a/Modules/Core/src/Controllers/mitkRenderingManager.cpp b/Modules/Core/src/Controllers/mitkRenderingManager.cpp index eb09085e41..42ed498f02 100644 --- a/Modules/Core/src/Controllers/mitkRenderingManager.cpp +++ b/Modules/Core/src/Controllers/mitkRenderingManager.cpp @@ -1,772 +1,774 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 namespace mitk { + itkEventMacroDefinition(RenderingManagerEvent, itk::AnyEvent); + itkEventMacroDefinition(RenderingManagerViewsInitializedEvent, RenderingManagerEvent); itkEventMacroDefinition(FocusChangedEvent, itk::AnyEvent); RenderingManager::Pointer RenderingManager::s_Instance = nullptr; RenderingManagerFactory *RenderingManager::s_RenderingManagerFactory = nullptr; RenderingManager::RenderingManager() : m_UpdatePending(false), m_MaxLOD(1), m_LODIncreaseBlocked(false), m_LODAbortMechanismEnabled(false), m_ClippingPlaneEnabled(false), m_TimeNavigationController(SliceNavigationController::New()), m_DataStorage(nullptr), m_ConstrainedPanningZooming(true), m_FocusedRenderWindow(nullptr), m_AntiAliasing(AntiAliasing::FastApproximate) { m_ShadingEnabled.assign(3, false); m_ShadingValues.assign(4, 0.0); InitializePropertyList(); } RenderingManager::~RenderingManager() { // Decrease reference counts of all registered vtkRenderWindows for // proper destruction RenderWindowVector::iterator it; for (it = m_AllRenderWindows.begin(); it != m_AllRenderWindows.end(); ++it) { (*it)->UnRegister(nullptr); auto callbacks_it = this->m_RenderWindowCallbacksList.find(*it); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { (*it)->RemoveObserver(callbacks_it->second.commands[0u]); (*it)->RemoveObserver(callbacks_it->second.commands[1u]); (*it)->RemoveObserver(callbacks_it->second.commands[2u]); } } } void RenderingManager::SetFactory(RenderingManagerFactory *factory) { s_RenderingManagerFactory = factory; } const RenderingManagerFactory *RenderingManager::GetFactory() { return s_RenderingManagerFactory; } bool RenderingManager::HasFactory() { if (RenderingManager::s_RenderingManagerFactory) { return true; } else { return false; } } RenderingManager::Pointer RenderingManager::New() { const RenderingManagerFactory *factory = GetFactory(); if (factory == nullptr) return nullptr; return factory->CreateRenderingManager(); } RenderingManager *RenderingManager::GetInstance() { if (!RenderingManager::s_Instance) { if (s_RenderingManagerFactory) { s_Instance = s_RenderingManagerFactory->CreateRenderingManager(); } } return s_Instance; } bool RenderingManager::IsInstantiated() { if (RenderingManager::s_Instance) return true; else return false; } void RenderingManager::AddRenderWindow(vtkRenderWindow *renderWindow) { if (renderWindow && (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.end())) { m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_AllRenderWindows.push_back(renderWindow); if (m_DataStorage.IsNotNull()) BaseRenderer::GetInstance(renderWindow)->SetDataStorage(m_DataStorage.GetPointer()); // Register vtkRenderWindow instance renderWindow->Register(nullptr); // Add callbacks for rendering abort mechanism // BaseRenderer *renderer = BaseRenderer::GetInstance( renderWindow ); vtkCallbackCommand *startCallbackCommand = vtkCallbackCommand::New(); startCallbackCommand->SetCallback(RenderingManager::RenderingStartCallback); renderWindow->AddObserver(vtkCommand::StartEvent, startCallbackCommand); vtkCallbackCommand *progressCallbackCommand = vtkCallbackCommand::New(); progressCallbackCommand->SetCallback(RenderingManager::RenderingProgressCallback); renderWindow->AddObserver(vtkCommand::AbortCheckEvent, progressCallbackCommand); vtkCallbackCommand *endCallbackCommand = vtkCallbackCommand::New(); endCallbackCommand->SetCallback(RenderingManager::RenderingEndCallback); renderWindow->AddObserver(vtkCommand::EndEvent, endCallbackCommand); RenderWindowCallbacks callbacks; callbacks.commands[0u] = startCallbackCommand; callbacks.commands[1u] = progressCallbackCommand; callbacks.commands[2u] = endCallbackCommand; this->m_RenderWindowCallbacksList[renderWindow] = callbacks; // Delete vtk variables correctly startCallbackCommand->Delete(); progressCallbackCommand->Delete(); endCallbackCommand->Delete(); } } void RenderingManager::RemoveRenderWindow(vtkRenderWindow *renderWindow) { if (m_RenderWindowList.erase(renderWindow)) { auto callbacks_it = this->m_RenderWindowCallbacksList.find(renderWindow); if (callbacks_it != this->m_RenderWindowCallbacksList.end()) { renderWindow->RemoveObserver(callbacks_it->second.commands[0u]); renderWindow->RemoveObserver(callbacks_it->second.commands[1u]); renderWindow->RemoveObserver(callbacks_it->second.commands[2u]); this->m_RenderWindowCallbacksList.erase(callbacks_it); } auto rw_it = std::find(m_AllRenderWindows.begin(), m_AllRenderWindows.end(), renderWindow); if (rw_it != m_AllRenderWindows.cend()) { // Decrease reference count for proper destruction (*rw_it)->UnRegister(nullptr); m_AllRenderWindows.erase(rw_it); } } } const RenderingManager::RenderWindowVector &RenderingManager::GetAllRegisteredRenderWindows() { return m_AllRenderWindows; } void RenderingManager::RequestUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } m_RenderWindowList[renderWindow] = RENDERING_REQUESTED; if (!m_UpdatePending) { m_UpdatePending = true; this->GenerateRenderingRequestEvent(); } } void RenderingManager::ForceImmediateUpdate(vtkRenderWindow *renderWindow) { // If the renderWindow is not valid, we do not want to inadvertantly create // an entry in the m_RenderWindowList map. It is possible if the user is // regularly calling AddRenderer and RemoveRenderer for a rendering update // to come into this method with a renderWindow pointer that is valid in the // sense that the window does exist within the application, but that // renderWindow has been temporarily removed from this RenderingManager for // performance reasons. if (m_RenderWindowList.find(renderWindow) == m_RenderWindowList.cend()) { return; } // Erase potentially pending requests for this window m_RenderWindowList[renderWindow] = RENDERING_INACTIVE; m_UpdatePending = false; // Immediately repaint this window (implementation platform specific) // If the size is 0 it crashes int *size = renderWindow->GetSize(); if (0 != size[0] && 0 != size[1]) { // prepare the camera etc. before rendering // Note: this is a very important step which should be called before the VTK render! // If you modify the camera anywhere else or after the render call, the scene cannot be seen. auto *vPR = dynamic_cast(BaseRenderer::GetInstance(renderWindow)); if (vPR) vPR->PrepareRender(); // Execute rendering renderWindow->Render(); } } void RenderingManager::RequestUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->RequestUpdate(it->first); } } } void RenderingManager::ForceImmediateUpdateAll(RequestType type) { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { int id = BaseRenderer::GetInstance(it->first)->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { // Immediately repaint this window (implementation platform specific) // If the size is 0, it crashes this->ForceImmediateUpdate(it->first); } } } void RenderingManager::InitializeViewsByBoundingObjects(const DataStorage* dataStorage) { if (nullptr == dataStorage) { return; } // get all nodes that have not set "includeInBoundingBox" to false auto pred = NodePredicateNot::New(NodePredicateProperty::New("includeInBoundingBox", BoolProperty::New(false))); DataStorage::SetOfObjects::ConstPointer filteredNodes = dataStorage->GetSubset(pred); TimeGeometry::ConstPointer boundingGeometry; if (!filteredNodes->empty()) { // calculate bounding geometry of these nodes boundingGeometry = dataStorage->ComputeBoundingGeometry3D(filteredNodes, "visible"); } // initialize the views to the bounding geometry this->InitializeViews(boundingGeometry); } bool RenderingManager::InitializeViews(const BaseGeometry *geometry, RequestType type, bool resetCamera) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return this->InitializeViews(propTimeGeometry, type, resetCamera); } bool RenderingManager::InitializeViews(const TimeGeometry *geometry, RequestType type, bool resetCamera) { bool boundingBoxInitialized = false; TimeGeometry::Pointer modifiedGeometry = nullptr; try { boundingBoxInitialized = this->ExtendGeometryForBoundingBox(geometry, modifiedGeometry); } catch (Exception& exception) { mitkReThrow(exception); } RenderWindowVector allRenderWindows = this->GetAllRegisteredRenderWindows(); RenderWindowVector::const_iterator it; for (it = allRenderWindows.cbegin(); it != allRenderWindows.cend(); ++it) { BaseRenderer *baseRenderer = BaseRenderer::GetInstance(*it); baseRenderer->SetConstrainZoomingAndPanning(this->GetConstrainedPanningZooming()); int id = baseRenderer->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->InternalViewInitialization(baseRenderer, modifiedGeometry, boundingBoxInitialized, id, resetCamera); } } if (boundingBoxInitialized) { this->GetTimeNavigationController()->SetInputWorldTimeGeometry(modifiedGeometry); } this->GetTimeNavigationController()->Update(); this->RequestUpdateAll(type); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return boundingBoxInitialized; } bool RenderingManager::InitializeViews(RequestType type) { const RenderWindowVector allRenderWindows = this->GetAllRegisteredRenderWindows(); RenderWindowVector::const_iterator it; for (it = allRenderWindows.cbegin(); it != allRenderWindows.cend(); ++it) { BaseRenderer *baseRenderer = BaseRenderer::GetInstance(*it); int id = baseRenderer->GetMapperID(); if ((type == REQUEST_UPDATE_ALL) || ((type == REQUEST_UPDATE_2DWINDOWS) && (id == 1)) || ((type == REQUEST_UPDATE_3DWINDOWS) && (id == 2))) { this->InternalViewInitialization(baseRenderer, nullptr, false, id, false); } } this->RequestUpdateAll(type); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return true; } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow, const BaseGeometry *geometry, bool initializeGlobalTime, bool resetCamera) { ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(dynamic_cast(geometry->Clone().GetPointer()), 1); return this->InitializeView(renderWindow, propTimeGeometry, initializeGlobalTime, resetCamera); } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow, const TimeGeometry *geometry, bool initializeGlobalTime, bool resetCamera) { bool boundingBoxInitialized = false; TimeGeometry::Pointer modifiedGeometry = nullptr; try { boundingBoxInitialized = this->ExtendGeometryForBoundingBox(geometry, modifiedGeometry); } catch (Exception &exception) { mitkReThrow(exception); } BaseRenderer *baseRenderer = BaseRenderer::GetInstance(renderWindow); baseRenderer->SetConstrainZoomingAndPanning(this->GetConstrainedPanningZooming()); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization(baseRenderer, modifiedGeometry, boundingBoxInitialized, id, resetCamera); if (boundingBoxInitialized && initializeGlobalTime) { this->GetTimeNavigationController()->SetInputWorldTimeGeometry(modifiedGeometry); } this->GetTimeNavigationController()->Update(); this->RequestUpdate(renderWindow); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return boundingBoxInitialized; } bool RenderingManager::InitializeView(vtkRenderWindow *renderWindow) { BaseRenderer *baseRenderer = BaseRenderer::GetInstance(renderWindow); int id = baseRenderer->GetMapperID(); this->InternalViewInitialization(baseRenderer, nullptr, false, id, false); this->RequestUpdate(renderWindow); // inform listeners that views have been initialized this->InvokeEvent(RenderingManagerViewsInitializedEvent()); return true; } void RenderingManager::InternalViewInitialization(BaseRenderer *baseRenderer, const TimeGeometry *geometry, bool boundingBoxInitialized, int mapperID, bool resetCamera) { SliceNavigationController *nc = baseRenderer->GetSliceNavigationController(); // Re-initialize view direction nc->SetViewDirectionToDefault(); if (boundingBoxInitialized) { // Set geometry for NC nc->SetInputWorldTimeGeometry(geometry); nc->Update(); if (resetCamera) { if (mapperID == BaseRenderer::Standard2D) { // For 2D SNCs, steppers are set so that the cross is centered in the image nc->GetSlice()->SetPos(nc->GetSlice()->GetSteps() / 2); baseRenderer->GetCameraController()->Fit(); } else if (mapperID == BaseRenderer::Standard3D) { baseRenderer->GetCameraController()->SetViewToAnterior(); } } } else { nc->Update(); } } bool RenderingManager::ExtendGeometryForBoundingBox(const TimeGeometry *geometry, TimeGeometry::Pointer& modifiedGeometry) { bool boundingBoxInitialized = false; if (nullptr == geometry) { return boundingBoxInitialized; } modifiedGeometry = geometry->Clone(); if (modifiedGeometry.IsNull()) { return boundingBoxInitialized; } if (modifiedGeometry->GetBoundingBoxInWorld()->GetDiagonalLength2() > eps) { boundingBoxInitialized = true; } // make sure bounding box has an extent bigger than zero in any direction for (TimeStepType step = 0; step < modifiedGeometry->CountTimeSteps(); ++step) { BaseGeometry::BoundsArrayType newBounds = modifiedGeometry->GetGeometryForTimeStep(step)->GetBounds(); for (unsigned int dimension = 0; (2 * dimension) < newBounds.Size(); dimension++) { // check for equality but for an epsilon if (Equal(newBounds[2 * dimension], newBounds[2 * dimension + 1])) { newBounds[2 * dimension + 1] += 1; if (Equal( newBounds[2 * dimension], newBounds[2 * dimension + 1])) // newBounds will still be equal if values are beyond double precision { mitkThrow() << "One dimension of object data has zero length, please make sure you're not using numbers " "beyond double precision as coordinates."; } } } modifiedGeometry->GetGeometryForTimeStep(step)->SetBounds(newBounds); } return boundingBoxInitialized; } const SliceNavigationController *RenderingManager::GetTimeNavigationController() const { return m_TimeNavigationController.GetPointer(); } SliceNavigationController *RenderingManager::GetTimeNavigationController() { return m_TimeNavigationController.GetPointer(); } void RenderingManager::ExecutePendingRequests() { m_UpdatePending = false; // Satisfy all pending update requests RenderWindowList::const_iterator it; int i = 0; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it, ++i) { if (it->second == RENDERING_REQUESTED) { this->ForceImmediateUpdate(it->first); } } } void RenderingManager::RenderingStartCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderingManager = RenderingManager::GetInstance(); auto renderWindow = dynamic_cast(caller); if (nullptr != renderWindow) renderingManager->m_RenderWindowList[renderWindow] = RENDERING_INPROGRESS; renderingManager->m_UpdatePending = false; } void RenderingManager::RenderingProgressCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderingManager = RenderingManager::GetInstance(); if (renderingManager->m_LODAbortMechanismEnabled) { auto renderWindow = dynamic_cast(caller); if (nullptr != renderWindow) { auto renderer = BaseRenderer::GetInstance(renderWindow); if (nullptr != renderer && 0 < renderer->GetNumberOfVisibleLODEnabledMappers()) renderingManager->DoMonitorRendering(); } } } void RenderingManager::RenderingEndCallback(vtkObject *caller, unsigned long, void *, void *) { auto renderWindow = dynamic_cast(caller); if (nullptr == renderWindow) { return; } auto renderer = BaseRenderer::GetInstance(renderWindow); if (nullptr == renderer) { return; } auto renderingManager = RenderingManager::GetInstance(); renderingManager->m_RenderWindowList[renderer->GetRenderWindow()] = RENDERING_INACTIVE; if (0 < renderer->GetNumberOfVisibleLODEnabledMappers()) { if (0 == renderingManager->m_NextLODMap[renderer]) { renderingManager->StartOrResetTimer(); } else { renderingManager->m_NextLODMap[renderer] = 0; } } } bool RenderingManager::IsRendering() const { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { return true; } } return false; } void RenderingManager::AbortRendering() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { if (it->second == RENDERING_INPROGRESS) { it->first->SetAbortRender(true); m_RenderingAbortedMap[BaseRenderer::GetInstance(it->first)] = true; } } } int RenderingManager::GetNextLOD(BaseRenderer *renderer) { if (renderer != nullptr) { return m_NextLODMap[renderer]; } else { return 0; } } void RenderingManager::ExecutePendingHighResRenderingRequest() { RenderWindowList::const_iterator it; for (it = m_RenderWindowList.cbegin(); it != m_RenderWindowList.cend(); ++it) { BaseRenderer *renderer = BaseRenderer::GetInstance(it->first); if (renderer->GetNumberOfVisibleLODEnabledMappers() > 0) { if (m_NextLODMap[renderer] == 0) { m_NextLODMap[renderer] = 1; RequestUpdate(it->first); } } } } void RenderingManager::SetMaximumLOD(unsigned int max) { m_MaxLOD = max; } // enable/disable shading void RenderingManager::SetShading(bool state, unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return; } m_ShadingEnabled[lod] = state; } bool RenderingManager::GetShading(unsigned int lod) { if (lod > m_MaxLOD) { itkWarningMacro(<< "LOD out of range requested: " << lod << " maxLOD: " << m_MaxLOD); return false; } return m_ShadingEnabled[lod]; } // enable/disable the clipping plane void RenderingManager::SetClippingPlaneStatus(bool status) { m_ClippingPlaneEnabled = status; } bool RenderingManager::GetClippingPlaneStatus() { return m_ClippingPlaneEnabled; } void RenderingManager::SetShadingValues(float ambient, float diffuse, float specular, float specpower) { m_ShadingValues[0] = ambient; m_ShadingValues[1] = diffuse; m_ShadingValues[2] = specular; m_ShadingValues[3] = specpower; } RenderingManager::FloatVector &RenderingManager::GetShadingValues() { return m_ShadingValues; } void RenderingManager::InitializePropertyList() { if (m_PropertyList.IsNull()) { m_PropertyList = PropertyList::New(); } this->SetProperty("coupled-zoom", BoolProperty::New(false)); this->SetProperty("coupled-plane-rotation", BoolProperty::New(false)); this->SetProperty("MIP-slice-rendering", BoolProperty::New(false)); } PropertyList::Pointer RenderingManager::GetPropertyList() const { return m_PropertyList; } BaseProperty *RenderingManager::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void RenderingManager::SetProperty(const char *propertyKey, BaseProperty *propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void RenderingManager::SetDataStorage(DataStorage *storage) { if (storage != nullptr) { m_DataStorage = storage; RenderingManager::RenderWindowVector::const_iterator iter; for (iter = m_AllRenderWindows.cbegin(); iter < m_AllRenderWindows.cend(); ++iter) { BaseRenderer::GetInstance((*iter))->SetDataStorage(m_DataStorage.GetPointer()); } } } void RenderingManager::SetRenderWindowFocus(vtkRenderWindow *focusWindow) { if (focusWindow != m_FocusedRenderWindow) { if (!focusWindow || (m_RenderWindowList.find(focusWindow) != m_RenderWindowList.cend())) { m_FocusedRenderWindow = focusWindow; this->InvokeEvent(FocusChangedEvent()); return; } MITK_ERROR << "Tried to set a RenderWindow that does not exist."; } } void RenderingManager::SetAntiAliasing(AntiAliasing antiAliasing) { if (m_AntiAliasing != antiAliasing) { auto renderingManager = RenderingManager::GetInstance(); auto renderWindows = renderingManager->GetAllRegisteredRenderWindows(); for (auto renderWindow : renderWindows) { auto renderers = renderWindow->GetRenderers(); if (nullptr != renderers) { renderers->InitTraversal(); auto renderer = renderers->GetNextItem(); while (nullptr != renderer) { renderer->SetUseFXAA(AntiAliasing::FastApproximate == antiAliasing); renderer = renderers->GetNextItem(); } renderingManager->RequestUpdate(renderWindow); } } m_AntiAliasing = antiAliasing; } } // Create and register generic RenderingManagerFactory. TestingRenderingManagerFactory renderingManagerFactory; } // namespace diff --git a/Modules/Core/src/Controllers/mitkSlicesCoordinator.cpp b/Modules/Core/src/Controllers/mitkSlicesCoordinator.cpp index db726896ce..be8f3d8c63 100644 --- a/Modules/Core/src/Controllers/mitkSlicesCoordinator.cpp +++ b/Modules/Core/src/Controllers/mitkSlicesCoordinator.cpp @@ -1,82 +1,84 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 namespace mitk { + itkEventMacroDefinition(SliceRotationEvent, itk::AnyEvent); + SlicesCoordinator::SlicesCoordinator() : m_LinkPlanes(true), m_MouseCursorSet(false) {} SlicesCoordinator::~SlicesCoordinator() {} void SlicesCoordinator::AddSliceController(SliceNavigationController *snc) { if (!snc) return; m_SliceNavigationControllers.push_back(snc); OnSliceControllerAdded(snc); // notify } void SlicesCoordinator::RemoveSliceController(SliceNavigationController *snc) { if (!snc) return; // see, whether snc is in our list SNCVector::iterator iter; for (iter = m_SliceNavigationControllers.begin(); iter != m_SliceNavigationControllers.end(); ++iter) if (*iter == snc) break; // if found, remove from list if (iter != m_SliceNavigationControllers.end()) { m_SliceNavigationControllers.erase(iter); OnSliceControllerRemoved(snc); } } void SlicesCoordinator::ResetMouseCursor() { if (m_MouseCursorSet) { ApplicationCursor::GetInstance()->PopCursor(); m_MouseCursorSet = false; } } void SlicesCoordinator::SetMouseCursor(const char *xpm[], int hotspotX, int hotspotY) { // Remove previously set mouse cursor if (m_MouseCursorSet) { ApplicationCursor::GetInstance()->PopCursor(); } ApplicationCursor::GetInstance()->PushCursor(xpm, hotspotX, hotspotY); m_MouseCursorSet = true; } void SlicesCoordinator::OnSliceControllerAdded(SliceNavigationController *) { // implement in subclasses } void SlicesCoordinator::OnSliceControllerRemoved(SliceNavigationController *) { // implement in subclasses } } // namespace diff --git a/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp b/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp index a859544e35..31eb0ac623 100644 --- a/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkAbstractTransformGeometry.cpp @@ -1,330 +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 "mitkAbstractTransformGeometry.h" #include mitk::AbstractTransformGeometry::AbstractTransformGeometry() : Superclass(), m_Plane(nullptr), m_FrameGeometry(nullptr) { Initialize(); m_ItkVtkAbstractTransform = itk::VtkAbstractTransform::New(); } mitk::AbstractTransformGeometry::AbstractTransformGeometry(const AbstractTransformGeometry &other) : Superclass(other), m_ParametricBoundingBox(other.m_ParametricBoundingBox) { if (other.m_ParametricBoundingBox.IsNotNull()) { m_ParametricBoundingBox = other.m_ParametricBoundingBox->DeepCopy(); this->SetParametricBounds(m_ParametricBoundingBox->GetBounds()); } this->SetPlane(other.m_Plane); this->SetFrameGeometry(other.m_FrameGeometry); m_ItkVtkAbstractTransform = itk::VtkAbstractTransform::New(); } mitk::AbstractTransformGeometry::~AbstractTransformGeometry() { } vtkAbstractTransform *mitk::AbstractTransformGeometry::GetVtkAbstractTransform() const { return m_ItkVtkAbstractTransform->GetVtkAbstractTransform(); } mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtentInMM(int direction) const { if (m_Plane.IsNull()) { itkExceptionMacro(<< "m_Plane is nullptr."); } return m_Plane->GetExtentInMM(direction); } const itk::Transform *mitk::AbstractTransformGeometry::GetParametricTransform() const { return m_ItkVtkAbstractTransform; } bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const { assert(this->IsBoundingBoxNull() == false); mitk::Point2D pt2d_mm; bool isInside; isInside = Map(pt3d_mm, pt2d_mm); Map(pt2d_mm, projectedPt3d_mm); return isInside; // Point3D pt3d_units; // pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); // pt3d_units[2] = 0; // projectedPt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_units); // return const_cast(m_BoundingBox.GetPointer())->IsInside(pt3d_units); } bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); Point3D pt3d_units; pt3d_units = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); return m_Plane->Map(pt3d_units, pt2d_mm); } void mitk::AbstractTransformGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); m_Plane->Map(pt2d_mm, pt3d_mm); pt3d_mm = m_ItkVtkAbstractTransform->TransformPoint(pt3d_mm); } bool mitk::AbstractTransformGeometry::Project(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by " "m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()"); assert(this->IsBoundingBoxNull() == false); - Vector3D vec3d_units; - vec3d_units = GetIndexToWorldTransform()->GetInverseMatrix() * vec3d_mm; + auto inverse = mitk::AffineTransform3D::New(); + GetIndexToWorldTransform()->GetInverse(inverse); + + Vector3D vec3d_units = inverse->GetMatrix() * vec3d_mm; vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); Point3D pt3d_units; mitk::ScalarType temp[3]; unsigned int i, j; for (j = 0; j < 3; ++j) temp[j] = atPt3d_mm[j] - GetIndexToWorldTransform()->GetOffset()[j]; for (i = 0; i < 3; ++i) { pt3d_units[i] = 0.0; for (j = 0; j < 3; ++j) - pt3d_units[i] += GetIndexToWorldTransform()->GetInverseMatrix()[i][j] * temp[j]; + pt3d_units[i] += inverse->GetMatrix()[i][j] * temp[j]; } return this->GetBoundingBox()->IsInside(pt3d_units); } bool mitk::AbstractTransformGeometry::Project(const mitk::Vector3D & /*vec3d_mm*/, mitk::Vector3D & /*projectedVec3d_mm*/) const { MITK_WARN << "Need additional point! No standard value defined. Please use Project(const mitk::Point3D & atPt3d_mm, " "const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm). Unfortunatley this one is not " "implemented at the moment. Sorry :("; itkExceptionMacro("not implemented yet - replace GetIndexToWorldTransform by " "m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()"); return false; } bool mitk::AbstractTransformGeometry::Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); ScalarType vtkpt[3], vtkvec[3]; itk2vtk(atPt3d_mm, vtkpt); itk2vtk(vec3d_mm, vtkvec); m_ItkVtkAbstractTransform->GetInverseVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); mitk::Vector3D vec3d_units; vtk2itk(vtkvec, vec3d_units); return m_Plane->Map(atPt3d_mm, vec3d_units, vec2d_mm); } void mitk::AbstractTransformGeometry::Map(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const { m_Plane->Map(atPt2d_mm, vec2d_mm, vec3d_mm); Point3D atPt3d_mm; Map(atPt2d_mm, atPt3d_mm); float vtkpt[3], vtkvec[3]; itk2vtk(atPt3d_mm, vtkpt); itk2vtk(vec3d_mm, vtkvec); m_ItkVtkAbstractTransform->GetVtkAbstractTransform()->TransformVectorAtPoint(vtkpt, vtkvec, vtkvec); vtk2itk(vtkvec, vec3d_mm); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D &pt_units, mitk::Point2D &pt_mm) const { m_Plane->IndexToWorld(pt_units, pt_mm); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D &pt_mm, mitk::Point2D &pt_units) const { m_Plane->WorldToIndex(pt_mm, pt_units); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Point2D & /*atPt2d_units*/, const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function AbstractTransformGeometry::IndexToWorld(point, vec, vec). Use " "AbstractTransformGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void mitk::AbstractTransformGeometry::IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const { m_Plane->IndexToWorld(vec_units, vec_mm); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Point2D & /*atPt2d_mm*/, const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function AbstractTransformGeometry::WorldToIndex(point, vec, vec). Use " "AbstractTransformGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void mitk::AbstractTransformGeometry::WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const { m_Plane->WorldToIndex(vec_mm, vec_units); } bool mitk::AbstractTransformGeometry::IsAbove(const mitk::Point3D &pt3d_mm, bool /*considerBoundingBox*/) const { assert((m_ItkVtkAbstractTransform.IsNotNull()) && (m_Plane.IsNotNull())); Point3D pt3d_ParametricWorld; pt3d_ParametricWorld = m_ItkVtkAbstractTransform->BackTransform(pt3d_mm); Point3D pt3d_ParametricUnits; ((BaseGeometry *)m_Plane)->WorldToIndex(pt3d_ParametricWorld, pt3d_ParametricUnits); return (pt3d_ParametricUnits[2] > m_ParametricBoundingBox->GetBounds()[4]); } void mitk::AbstractTransformGeometry::SetVtkAbstractTransform(vtkAbstractTransform *aVtkAbstractTransform) { m_ItkVtkAbstractTransform->SetVtkAbstractTransform(aVtkAbstractTransform); } void mitk::AbstractTransformGeometry::SetPlane(const mitk::PlaneGeometry *aPlane) { if (aPlane != nullptr) { m_Plane = static_cast(aPlane->Clone().GetPointer()); BoundingBox::BoundsArrayType b = m_Plane->GetBoundingBox()->GetBounds(); SetParametricBounds(b); CalculateFrameGeometry(); } else { if (m_Plane.IsNull()) return; m_Plane = nullptr; } Modified(); } void mitk::AbstractTransformGeometry::CalculateFrameGeometry() { if ((m_Plane.IsNull()) || (m_FrameGeometry.IsNotNull())) return; //@warning affine-transforms and bounding-box should be set by specific sub-classes! SetBounds(m_Plane->GetBoundingBox()->GetBounds()); } void mitk::AbstractTransformGeometry::SetFrameGeometry(const mitk::BaseGeometry *frameGeometry) { if ((frameGeometry != nullptr) && (frameGeometry->IsValid())) { m_FrameGeometry = static_cast(frameGeometry->Clone().GetPointer()); SetIndexToWorldTransform(m_FrameGeometry->GetIndexToWorldTransform()); SetBounds(m_FrameGeometry->GetBounds()); } else { m_FrameGeometry = nullptr; } } -unsigned long mitk::AbstractTransformGeometry::GetMTime() const +itk::ModifiedTimeType mitk::AbstractTransformGeometry::GetMTime() const { if (Superclass::GetMTime() < m_ItkVtkAbstractTransform->GetMTime()) return m_ItkVtkAbstractTransform->GetMTime(); return Superclass::GetMTime(); } void mitk::AbstractTransformGeometry::SetOversampling(mitk::ScalarType oversampling) { if (m_Plane.IsNull()) { itkExceptionMacro(<< "m_Plane is not set."); } mitk::BoundingBox::BoundsArrayType bounds = m_Plane->GetBounds(); bounds[1] *= oversampling; bounds[3] *= oversampling; bounds[5] *= oversampling; SetParametricBounds(bounds); } itk::LightObject::Pointer mitk::AbstractTransformGeometry::InternalClone() const { Self::Pointer newGeometry = new AbstractTransformGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void mitk::AbstractTransformGeometry::SetParametricBounds(const BoundingBox::BoundsArrayType &bounds) { m_ParametricBoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < GetNDimensions(); ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } m_ParametricBoundingBox->SetPoints(pointscontainer); m_ParametricBoundingBox->ComputeBoundingBox(); this->Modified(); } const mitk::BoundingBox::BoundsArrayType &mitk::AbstractTransformGeometry::GetParametricBounds() const { assert(m_ParametricBoundingBox.IsNotNull()); return m_ParametricBoundingBox->GetBounds(); } mitk::ScalarType mitk::AbstractTransformGeometry::GetParametricExtent(int direction) const { if (direction < 0 || direction >= 3) mitkThrow() << "Invalid direction. Must be between either 0, 1 or 2. "; assert(m_ParametricBoundingBox.IsNotNull()); BoundingBoxType::BoundsArrayType bounds = m_ParametricBoundingBox->GetBounds(); return bounds[direction * 2 + 1] - bounds[direction * 2]; } diff --git a/Modules/Core/src/DataManagement/mitkBaseData.cpp b/Modules/Core/src/DataManagement/mitkBaseData.cpp index 9f3b670884..3750bd4848 100644 --- a/Modules/Core/src/DataManagement/mitkBaseData.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseData.cpp @@ -1,348 +1,348 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkBaseData.h" #include #include #include #include #include mitk::BaseData::BaseData() : m_SourceOutputIndexDuplicate(0), m_Initialized(true), m_PropertyList(PropertyList::New()), m_TimeGeometry(ProportionalTimeGeometry::New()) { } mitk::BaseData::BaseData(const BaseData &other) : itk::DataObject(), OperationActor(), Identifiable(), m_SourceOutputIndexDuplicate(other.m_SourceOutputIndexDuplicate), m_Initialized(other.m_Initialized), m_PropertyList(other.m_PropertyList->Clone()), m_TimeGeometry(other.m_TimeGeometry->Clone()) { } mitk::BaseData::~BaseData() { } void mitk::BaseData::InitializeTimeGeometry(unsigned int timeSteps) { mitk::Geometry3D::Pointer geo3D = mitk::Geometry3D::New(); mitk::BaseGeometry::Pointer baseGeo = dynamic_cast(geo3D.GetPointer()); baseGeo->Initialize(); // The geometry is propagated automatically to the other items, // if EvenlyTimed is true... // Old timeGeometry->InitializeEvenlyTimed( g3d.GetPointer(), timeSteps ); TimeGeometry::Pointer timeGeometry = this->GetTimeGeometry(); timeGeometry->Initialize(); timeGeometry->Expand(timeSteps); for (TimeStepType step = 0; step < timeSteps; ++step) { timeGeometry->SetTimeStepGeometry(baseGeo.GetPointer(), step); } } void mitk::BaseData::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } if (m_TimeGeometry.IsNotNull()) { m_TimeGeometry->UpdateBoundingBox(); } } const mitk::TimeGeometry *mitk::BaseData::GetUpdatedTimeGeometry() { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetTimeGeometry(); } void mitk::BaseData::Expand(unsigned int timeSteps) { if (m_TimeGeometry.IsNotNull()) { m_TimeGeometry->Expand(timeSteps); } else { this->InitializeTimeGeometry(timeSteps); } } const mitk::BaseGeometry *mitk::BaseData::GetUpdatedGeometry(int t) { SetRequestedRegionToLargestPossibleRegion(); UpdateOutputInformation(); return GetGeometry(t); } void mitk::BaseData::SetGeometry(BaseGeometry *geometry) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); if (geometry != nullptr) { timeGeometry->Initialize(geometry, 1); } SetTimeGeometry(timeGeometry); return; } void mitk::BaseData::SetTimeGeometry(TimeGeometry *geometry) { m_TimeGeometry = geometry; this->Modified(); } void mitk::BaseData::SetClonedGeometry(const BaseGeometry *aGeometry3D) { SetGeometry(static_cast(aGeometry3D->Clone().GetPointer())); } void mitk::BaseData::SetClonedTimeGeometry(const TimeGeometry *geometry) { TimeGeometry::Pointer clonedGeometry = geometry->Clone(); SetTimeGeometry(clonedGeometry.GetPointer()); } void mitk::BaseData::SetClonedGeometry(const BaseGeometry *aGeometry3D, unsigned int time) { if (m_TimeGeometry) { m_TimeGeometry->SetTimeStepGeometry(static_cast(aGeometry3D->Clone().GetPointer()), time); } } bool mitk::BaseData::IsEmptyTimeStep(unsigned int) const { return IsInitialized() == false; } bool mitk::BaseData::IsEmpty() const { if (IsInitialized() == false) return true; const TimeGeometry *timeGeometry = const_cast(this)->GetUpdatedTimeGeometry(); if (timeGeometry == nullptr) return true; unsigned int timeSteps = timeGeometry->CountTimeSteps(); for (unsigned int t = 0; t < timeSteps; ++t) { if (IsEmptyTimeStep(t) == false) return false; } return true; } itk::SmartPointer mitk::BaseData::GetSource() const { return static_cast(Superclass::GetSource().GetPointer()); } mitk::PropertyList::Pointer mitk::BaseData::GetPropertyList() const { return m_PropertyList; } mitk::BaseProperty::Pointer mitk::BaseData::GetProperty(const char *propertyKey) const { return m_PropertyList->GetProperty(propertyKey); } void mitk::BaseData::SetProperty(const char *propertyKey, BaseProperty *propertyValue) { m_PropertyList->SetProperty(propertyKey, propertyValue); } void mitk::BaseData::SetPropertyList(PropertyList *pList) { m_PropertyList = pList; } void mitk::BaseData::SetOrigin(const mitk::Point3D &origin) { TimeGeometry *timeGeom = GetTimeGeometry(); assert(timeGeom != nullptr); TimeStepType steps = timeGeom->CountTimeSteps(); for (TimeStepType timestep = 0; timestep < steps; ++timestep) { auto geometry = GetGeometry(timestep); if (geometry != nullptr) { geometry->SetOrigin(origin); } } } -unsigned long mitk::BaseData::GetMTime() const +itk::ModifiedTimeType mitk::BaseData::GetMTime() const { - unsigned long time = Superclass::GetMTime(); + auto time = Superclass::GetMTime(); if (m_TimeGeometry.IsNotNull()) { if ((time < m_TimeGeometry->GetMTime())) { return m_TimeGeometry->GetMTime(); } } return time; } void mitk::BaseData::Graft(const itk::DataObject *) { itkExceptionMacro(<< "Graft not implemented for mitk::BaseData subclass " << this->GetNameOfClass()) } void mitk::BaseData::CopyInformation(const itk::DataObject *data) { const auto *bd = dynamic_cast(data); if (bd != nullptr) { m_PropertyList = bd->GetPropertyList()->Clone(); if (bd->GetTimeGeometry() != nullptr) { m_TimeGeometry = bd->GetTimeGeometry()->Clone(); } } else { // pointer could not be cast back down; this can be the case if your filters input // and output objects differ in type; then you have to write your own GenerateOutputInformation method itkExceptionMacro(<< "mitk::BaseData::CopyInformation() cannot cast " << typeid(data).name() << " to " << typeid(Self *).name()); } } bool mitk::BaseData::IsInitialized() const { return m_Initialized; } void mitk::BaseData::Clear() { this->ClearData(); this->InitializeEmpty(); } void mitk::BaseData::ClearData() { if (m_Initialized) { ReleaseData(); m_Initialized = false; } } void mitk::BaseData::ExecuteOperation(mitk::Operation * /*operation*/) { // empty by default. override if needed! } void mitk::BaseData::PrintSelf(std::ostream &os, itk::Indent indent) const { os << std::endl; os << indent << " TimeGeometry: "; if (GetTimeGeometry() == nullptr) os << "nullptr" << std::endl; else GetTimeGeometry()->Print(os, indent); // print out all properties PropertyList::Pointer propertyList = this->GetPropertyList(); if (propertyList.IsNotNull() && !propertyList->IsEmpty()) { // general headline os << "Properties of BaseData:" << std::endl; const PropertyList::PropertyMap *map = propertyList->GetMap(); for (auto iter = map->begin(); iter != map->end(); ++iter) { os << " " << (*iter).first << " " << (*iter).second->GetValueAsString() << std::endl; } } } mitk::BaseProperty::ConstPointer mitk::BaseData::GetConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) const { if (propertyKey.empty()) return nullptr; if (contextName.empty() || fallBackOnDefaultContext) return m_PropertyList->GetProperty(propertyKey); return nullptr; } mitk::BaseProperty * mitk::BaseData::GetNonConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) return nullptr; if (contextName.empty() || fallBackOnDefaultContext) return m_PropertyList->GetProperty(propertyKey); return nullptr; } void mitk::BaseData::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (contextName.empty() || fallBackOnDefaultContext) { m_PropertyList->SetProperty(propertyKey, property); return; } mitkThrow() << "Unknown or unsupported non-default property context."; } void mitk::BaseData::RemoveProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (contextName.empty() || fallBackOnDefaultContext) { m_PropertyList->RemoveProperty(propertyKey); return; } mitkThrow() << "Unknown or unsupported non-default property context."; } std::vector mitk::BaseData::GetPropertyKeys(const std::string &/*contextName*/, bool /*includeDefaultContext*/) const { return m_PropertyList->GetPropertyKeys(); } std::vector mitk::BaseData::GetPropertyContextNames() const { return std::vector(); } diff --git a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp index f1323caf3f..09493418f5 100644 --- a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp @@ -1,1097 +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 #include #include #include #include #include "mitkApplyTransformMatrixOperation.h" #include "mitkBaseGeometry.h" #include "mitkGeometryTransformHolder.h" #include "mitkInteractionConst.h" #include "mitkMatrixConvert.h" #include "mitkModifiedLock.h" #include "mitkPointOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkRotationOperation.h" #include "mitkScaleOperation.h" #include "mitkVector.h" #include "mitkMatrix.h" mitk::BaseGeometry::BaseGeometry() : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0), m_ImageGeometry(false), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(); Initialize(); } mitk::BaseGeometry::BaseGeometry(const BaseGeometry &other) : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_ImageGeometry(other.m_ImageGeometry), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(*other.GetGeometryTransformHolder()); other.InitializeGeometry(this); } mitk::BaseGeometry::~BaseGeometry() { delete m_GeometryTransform; } void mitk::BaseGeometry::SetVtkMatrixDeepCopy(vtkTransform *vtktransform) { m_GeometryTransform->SetVtkMatrixDeepCopy(vtktransform); } const mitk::Point3D mitk::BaseGeometry::GetOrigin() const { return m_GeometryTransform->GetOrigin(); } void mitk::BaseGeometry::SetOrigin(const Point3D &origin) { mitk::ModifiedLock lock(this); if (origin != GetOrigin()) { m_GeometryTransform->SetOrigin(origin); Modified(); } } const mitk::Vector3D mitk::BaseGeometry::GetSpacing() const { return m_GeometryTransform->GetSpacing(); } void mitk::BaseGeometry::Initialize() { float b[6] = {0, 1, 0, 1, 0, 1}; SetFloatBounds(b); m_GeometryTransform->Initialize(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; } void mitk::BaseGeometry::SetFloatBounds(const float bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } void mitk::BaseGeometry::SetFloatBounds(const double bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const double *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } /** Initialize the geometry */ void mitk::BaseGeometry::InitializeGeometry(BaseGeometry *newGeometry) const { newGeometry->SetBounds(m_BoundingBox->GetBounds()); newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); newGeometry->InitializeGeometryTransformHolder(this); newGeometry->m_ImageGeometry = m_ImageGeometry; } void mitk::BaseGeometry::InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry) { this->m_GeometryTransform->Initialize(otherGeometry->GetGeometryTransformHolder()); } /** Set the bounds */ void mitk::BaseGeometry::SetBounds(const BoundsArrayType &bounds) { mitk::ModifiedLock lock(this); this->CheckBounds(bounds); m_BoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < m_NDimensions; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } m_BoundingBox->SetPoints(pointscontainer); m_BoundingBox->ComputeBoundingBox(); this->Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D *transform) { mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransform(transform); Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform) { // security check mitk::Vector3D originalSpacing = this->GetSpacing(); mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransformWithoutChangingSpacing(transform); Modified(); // Security check. Spacig must not have changed if (!mitk::Equal(originalSpacing, this->GetSpacing())) { MITK_WARN << "Spacing has changed in a method, where the spacing must not change."; assert(false); } } const mitk::BaseGeometry::BoundsArrayType mitk::BaseGeometry::GetBounds() const { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox->GetBounds(); } bool mitk::BaseGeometry::IsValid() const { return true; } void mitk::BaseGeometry::SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { PreSetSpacing(aSpacing); _SetSpacing(aSpacing, enforceSetSpacing); } void mitk::BaseGeometry::_SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { m_GeometryTransform->SetSpacing(aSpacing, enforceSetSpacing); } mitk::Vector3D mitk::BaseGeometry::GetAxisVector(unsigned int direction) const { Vector3D frontToBack; - frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction)); + frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).as_ref()); frontToBack *= GetExtent(direction); return frontToBack; } mitk::ScalarType mitk::BaseGeometry::GetExtent(unsigned int direction) const { assert(m_BoundingBox.IsNotNull()); if (direction >= m_NDimensions) mitkThrow() << "Direction is too big. This geometry is for 3D Data"; BoundsArrayType bounds = m_BoundingBox->GetBounds(); return bounds[direction * 2 + 1] - bounds[direction * 2]; } bool mitk::BaseGeometry::Is2DConvertable() { bool isConvertableWithoutLoss = true; do { if (this->GetSpacing()[2] != 1) { isConvertableWithoutLoss = false; break; } if (this->GetOrigin()[2] != 0) { isConvertableWithoutLoss = false; break; } mitk::Vector3D col0, col1, col2; - col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); - col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); - col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); + col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref()); + col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref()); + col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref()); if ((col0[2] != 0) || (col1[2] != 0) || (col2[0] != 0) || (col2[1] != 0) || (col2[2] != 1)) { isConvertableWithoutLoss = false; break; } } while (false); return isConvertableWithoutLoss; } mitk::Point3D mitk::BaseGeometry::GetCenter() const { assert(m_BoundingBox.IsNotNull()); Point3D c = m_BoundingBox->GetCenter(); if (m_ImageGeometry) { // Get Center returns the middel of min and max pixel index. In corner based images, this is the right position. // In center based images (imageGeometry == true), the index needs to be shifted back. c[0] -= 0.5; c[1] -= 0.5; c[2] -= 0.5; } this->IndexToWorld(c, c); return c; } double mitk::BaseGeometry::GetDiagonalLength2() const { Vector3D diagonalvector = GetCornerPoint() - GetCornerPoint(false, false, false); return diagonalvector.GetSquaredNorm(); } double mitk::BaseGeometry::GetDiagonalLength() const { return sqrt(GetDiagonalLength2()); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(int id) const { assert(id >= 0); assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; switch (id) { case 0: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[4]); break; case 1: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[5]); break; case 2: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[4]); break; case 3: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[5]); break; case 4: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[4]); break; case 5: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[5]); break; case 6: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[4]); break; case 7: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[5]); break; default: { itkExceptionMacro(<< "A cube only has 8 corners. These are labeled 0-7."); } } if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::ScalarType mitk::BaseGeometry::GetExtentInMM(int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).magnitude() * GetExtent(direction); } void mitk::BaseGeometry::SetExtentInMM(int direction, ScalarType extentInMM) { mitk::ModifiedLock lock(this); ScalarType len = GetExtentInMM(direction); if (fabs(len - extentInMM) >= mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_GeometryTransform->GetVnlMatrix(); if (len > extentInMM) vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) / len * extentInMM); else vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) * extentInMM / len); Matrix3D matrix; matrix = vnlmatrix; m_GeometryTransform->SetMatrix(matrix); Modified(); } } bool mitk::BaseGeometry::IsInside(const mitk::Point3D &p) const { mitk::Point3D index; WorldToIndex(p, index); return IsIndexInside(index); } bool mitk::BaseGeometry::IsIndexInside(const mitk::Point3D &index) const { bool inside = false; // if it is an image geometry, we need to convert the index to discrete values // this is done by applying the rounding function also used in WorldToIndex (see line 323) if (m_ImageGeometry) { mitk::Point3D discretIndex; discretIndex[0] = itk::Math::RoundHalfIntegerUp(index[0]); discretIndex[1] = itk::Math::RoundHalfIntegerUp(index[1]); discretIndex[2] = itk::Math::RoundHalfIntegerUp(index[2]); inside = this->GetBoundingBox()->IsInside(discretIndex); // we have to check if the index is at the upper border of each dimension, // because the boundingbox is not centerbased if (inside) { const BoundingBox::BoundsArrayType &bounds = this->GetBoundingBox()->GetBounds(); if ((discretIndex[0] == bounds[1]) || (discretIndex[1] == bounds[3]) || (discretIndex[2] == bounds[5])) inside = false; } } else inside = this->GetBoundingBox()->IsInside(index); return inside; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const { mitk::Vector3D tempIn, tempOut; const TransformType::OffsetType &offset = this->GetIndexToWorldTransform()->GetOffset(); tempIn = pt_mm.GetVectorFromOrigin() - offset; WorldToIndex(tempIn, tempOut); pt_units = Point3D(tempOut); } void mitk::BaseGeometry::WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != this->GetIndexToWorldTransform()->GetMTime()) { if (!m_InvertedTransform) { m_InvertedTransform = TransformType::New(); } if (!this->GetIndexToWorldTransform()->GetInverse(m_InvertedTransform.GetPointer())) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed."); } m_IndexToWorldTransformLastModified = this->GetIndexToWorldTransform()->GetMTime(); } // Check for valid matrix inversion const TransformType::MatrixType &inverse = m_InvertedTransform->GetMatrix(); if (inverse.GetVnlMatrix().has_nans()) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl << this->GetIndexToWorldTransform()->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse); } vec_units = inverse * vec_mm; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::WorldToIndex(point, vec, vec). Use " "BaseGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } mitk::VnlVector mitk::BaseGeometry::GetOriginVnl() const { return GetOrigin().GetVnlVector(); } vtkLinearTransform *mitk::BaseGeometry::GetVtkTransform() const { return m_GeometryTransform->GetVtkTransform(); } void mitk::BaseGeometry::SetIdentity() { mitk::ModifiedLock lock(this); m_GeometryTransform->SetIdentity(); Modified(); } void mitk::BaseGeometry::Compose(const mitk::BaseGeometry::TransformType *other, bool pre) { mitk::ModifiedLock lock(this); m_GeometryTransform->Compose(other, pre); Modified(); } void mitk::BaseGeometry::Compose(const vtkMatrix4x4 *vtkmatrix, bool pre) { mitk::BaseGeometry::TransformType::Pointer itkTransform = mitk::BaseGeometry::TransformType::New(); TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); Compose(itkTransform, pre); } void mitk::BaseGeometry::Translate(const Vector3D &vector) { if ((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) { this->SetOrigin(this->GetOrigin() + vector); } } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const { pt_mm = this->GetIndexToWorldTransform()->TransformPoint(pt_units); } void mitk::BaseGeometry::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { vec_mm = this->GetIndexToWorldTransform()->TransformVector(vec_units); } void mitk::BaseGeometry::ExecuteOperation(Operation *operation) { mitk::ModifiedLock lock(this); vtkTransform *vtktransform = vtkTransform::New(); vtktransform->SetMatrix(this->GetVtkMatrix()); switch (operation->GetOperationType()) { case OpNOTHING: break; case OpMOVE: { auto *pointOp = dynamic_cast(operation); if (pointOp == nullptr) { MITK_ERROR << "Point move operation is null!"; return; } mitk::Point3D newPos = pointOp->GetPoint(); ScalarType data[3]; vtktransform->GetPosition(data); vtktransform->PostMultiply(); vtktransform->Translate(newPos[0], newPos[1], newPos[2]); vtktransform->PreMultiply(); break; } case OpSCALE: { auto *scaleOp = dynamic_cast(operation); if (scaleOp == nullptr) { MITK_ERROR << "Scale operation is null!"; return; } mitk::Point3D newScale = scaleOp->GetScaleFactor(); ScalarType scalefactor[3]; scalefactor[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); scalefactor[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); scalefactor[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); mitk::Point3D anchor = scaleOp->GetScaleAnchorPoint(); vtktransform->PostMultiply(); vtktransform->Translate(-anchor[0], -anchor[1], -anchor[2]); vtktransform->Scale(scalefactor[0], scalefactor[1], scalefactor[2]); vtktransform->Translate(anchor[0], anchor[1], anchor[2]); break; } case OpROTATE: { auto *rotateOp = dynamic_cast(operation); if (rotateOp == nullptr) { MITK_ERROR << "Rotation operation is null!"; return; } Vector3D rotationVector = rotateOp->GetVectorOfRotation(); Point3D center = rotateOp->GetCenterOfRotation(); ScalarType angle = rotateOp->GetAngleOfRotation(); vtktransform->PostMultiply(); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]); vtktransform->Translate(center[0], center[1], center[2]); vtktransform->PreMultiply(); break; } case OpRESTOREPLANEPOSITION: { // Copy necessary to avoid vtk warning vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); TransferItkTransformToVtkMatrix( dynamic_cast(operation)->GetTransform().GetPointer(), matrix); vtktransform->SetMatrix(matrix); matrix->Delete(); break; } case OpAPPLYTRANSFORMMATRIX: { auto *applyMatrixOp = dynamic_cast(operation); vtktransform->SetMatrix(applyMatrixOp->GetMatrix()); break; } default: vtktransform->Delete(); return; } this->SetVtkMatrixDeepCopy(vtktransform); Modified(); vtktransform->Delete(); } mitk::VnlVector mitk::BaseGeometry::GetMatrixColumn(unsigned int direction) const { - return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction); + return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).as_ref(); } mitk::BoundingBox::Pointer mitk::BaseGeometry::CalculateBoundingBoxRelativeToTransform( const mitk::AffineTransform3D *transform) const { mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid = 0; unsigned char i; if (transform != nullptr) { mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); transform->GetInverse(inverse); for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, inverse->TransformPoint(GetCornerPoint(i))); } else { for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, GetCornerPoint(i)); } mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } const std::string mitk::BaseGeometry::GetTransformAsString(TransformType *transformType) { std::ostringstream out; out << '['; for (int i = 0; i < 3; ++i) { out << '['; for (int j = 0; j < 3; ++j) out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' '; out << ']'; } out << "]["; for (int i = 0; i < 3; ++i) out << transformType->GetOffset()[i] << ' '; out << "]\0"; return out.str(); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkmatrix); } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D & /*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::IndexToWorld(point, vec, vec). Use " "BaseGeometry::IndexToWorld(vec, vec) instead!"; // vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); this->IndexToWorld(vec_units, vec_mm); } vtkMatrix4x4 *mitk::BaseGeometry::GetVtkMatrix() { return m_GeometryTransform->GetVtkMatrix(); } bool mitk::BaseGeometry::IsBoundingBoxNull() const { return m_BoundingBox.IsNull(); } bool mitk::BaseGeometry::IsIndexToWorldTransformNull() const { return m_GeometryTransform->IsIndexToWorldTransformNull(); } void mitk::BaseGeometry::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) { // If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because // imageGeometries origins are pixel-center-based // ... and remove the offset, if you switch an imageGeometry back to a normal geometry // For more information please see the Geometry documentation page if (m_ImageGeometry == isAnImageGeometry) return; const BoundingBox::BoundsArrayType &boundsarray = this->GetBoundingBox()->GetBounds(); Point3D originIndex; FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]); if (isAnImageGeometry == true) FillVector3D(originIndex, originIndex[0] + 0.5, originIndex[1] + 0.5, originIndex[2] + 0.5); else FillVector3D(originIndex, originIndex[0] - 0.5, originIndex[1] - 0.5, originIndex[2] - 0.5); Point3D originWorld; originWorld = GetIndexToWorldTransform()->TransformPoint(originIndex); // instead could as well call IndexToWorld(originIndex,originWorld); SetOrigin(originWorld); this->SetImageGeometry(isAnImageGeometry); } void mitk::BaseGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; if (this->IsIndexToWorldTransformNull()) os << "nullptr" << std::endl; else { // from itk::MatrixOffsetTransformBase unsigned int i, j; os << std::endl; os << indent << "Matrix: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << this->GetIndexToWorldTransform()->GetMatrix()[i][j] << " "; } os << std::endl; } os << indent << "Offset: " << this->GetIndexToWorldTransform()->GetOffset() << std::endl; os << indent << "Center: " << this->GetIndexToWorldTransform()->GetCenter() << std::endl; os << indent << "Translation: " << this->GetIndexToWorldTransform()->GetTranslation() << std::endl; - os << indent << "Inverse: " << std::endl; - for (i = 0; i < 3; i++) + auto inverse = mitk::AffineTransform3D::New(); + if (this->GetIndexToWorldTransform()->GetInverse(inverse)) { - os << indent.GetNextIndent(); - for (j = 0; j < 3; j++) + os << indent << "Inverse: " << std::endl; + for (i = 0; i < 3; i++) { - os << this->GetIndexToWorldTransform()->GetInverseMatrix()[i][j] << " "; + os << indent.GetNextIndent(); + for (j = 0; j < 3; j++) + { + os << inverse->GetMatrix()[i][j] << " "; + } + os << std::endl; } - os << std::endl; } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { os << this->GetIndexToWorldTransform()->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; if (this->IsBoundingBoxNull()) os << "nullptr" << std::endl; else { os << indent << "( "; for (unsigned int i = 0; i < 3; i++) { os << this->GetBoundingBox()->GetBounds()[2 * i] << "," << this->GetBoundingBox()->GetBounds()[2 * i + 1] << " "; } os << " )" << std::endl; } os << indent << " Origin: " << this->GetOrigin() << std::endl; os << indent << " ImageGeometry: " << this->GetImageGeometry() << std::endl; os << indent << " Spacing: " << this->GetSpacing() << std::endl; } void mitk::BaseGeometry::Modified() const { if (!m_ModifiedLockFlag) Superclass::Modified(); else m_ModifiedCalledFlag = true; } mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() const { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::GeometryTransformHolder *mitk::BaseGeometry::GetGeometryTransformHolder() const { return m_GeometryTransform; } bool mitk::Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose) { bool result = true; BaseGeometry::BoundsArrayType rightBounds = rightHandSide.GetBounds(); BaseGeometry::BoundsArrayType leftBounds = leftHandSide.GetBounds(); BaseGeometry::BoundsArrayType::Iterator itLeft = leftBounds.Begin(); for (BaseGeometry::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight) { if ((!mitk::Equal(*itLeft, *itRight, eps))) { if (verbose) { MITK_INFO << "[( Geometry3D::BoundingBoxType )] bounds are not equal."; MITK_INFO << "rightHandSide is " << setprecision(12) << *itRight << " : leftHandSide is " << *itLeft << " and tolerance is " << eps; } result = false; } itLeft++; } return result; } bool mitk::Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, ScalarType coordinateEps, ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings if (!mitk::Equal(leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is " << leftHandSide.GetSpacing() << " and tolerance is " << coordinateEps; } result = false; } // Compare Origins if (!mitk::Equal(leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Origin differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is " << leftHandSide.GetOrigin() << " and tolerance is " << coordinateEps; } result = false; } // Compare Axis and Extents for (unsigned int i = 0; i < 3; ++i) { if (!mitk::Equal(leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), directionEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] AxisVector #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetAxisVector(i) << " : leftHandSide is " << leftHandSide.GetAxisVector(i) << " and tolerance is " << directionEps; } result = false; } if (!mitk::Equal(leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Extent #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetExtent(i) << " : leftHandSide is " << leftHandSide.GetExtent(i) << " and tolerance is " << coordinateEps; } result = false; } } // Compare ImageGeometry Flag if (rightHandSide.GetImageGeometry() != leftHandSide.GetImageGeometry()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetImageGeometry() << " : leftHandSide is " << leftHandSide.GetImageGeometry(); } result = false; } // Compare FrameOfReference ID if (rightHandSide.GetFrameOfReferenceID() != leftHandSide.GetFrameOfReferenceID()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetFrameOfReferenceID is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetFrameOfReferenceID() << " : leftHandSide is " << leftHandSide.GetFrameOfReferenceID(); } result = false; } // Compare BoundingBoxes if (!mitk::Equal(*leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), coordinateEps, verbose)) { result = false; } // Compare IndexToWorldTransform Matrix if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), directionEps, verbose)) { result = false; } return result; } bool mitk::Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType eps, bool verbose) { return Equal(leftHandSide, rightHandSide, eps, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose) { // Compare IndexToWorldTransform Matrix if (!mitk::MatrixEqualElementWise(leftHandSide.GetMatrix(), rightHandSide.GetMatrix(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D::TransformType )] Index to World Transformation matrix differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMatrix() << " : leftHandSide is " << leftHandSide.GetMatrix() << " and tolerance is " << eps; } return false; } return true; } bool mitk::IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, ScalarType coordinateEps, ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings (must be equal) const auto testedSpacing = testGeo.GetSpacing(); if (!mitk::Equal(testedSpacing, referenceGeo.GetSpacing(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "testedGeometry is " << setprecision(12) << testedSpacing << " : referenceGeometry is " << referenceGeo.GetSpacing() << " and tolerance is " << coordinateEps; } result = false; } // Compare ImageGeometry Flag (must be equal) if (referenceGeo.GetImageGeometry() != testGeo.GetImageGeometry()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "referenceGeo is " << referenceGeo.GetImageGeometry() << " : testGeo is " << testGeo.GetImageGeometry(); } result = false; } // Compare IndexToWorldTransform Matrix (must be equal -> same axis directions) if (!Equal(*(testGeo.GetIndexToWorldTransform()), *(referenceGeo.GetIndexToWorldTransform()), directionEps, verbose)) { result = false; } //check if the geometry is within or equal to the bounds of reference geomentry. for (int i = 0; i<8; ++i) { auto testCorner = testGeo.GetCornerPoint(i); bool isInside = false; mitk::Point3D testCornerIndex; referenceGeo.WorldToIndex(testCorner, testCornerIndex); std::bitset bs(i); //To regard the coordinateEps, we substract or add it to the index elements //depending on wether it was constructed by a lower or an upper bound value //(see implementation of BaseGeometry::GetCorner()). if (bs.test(0)) { testCornerIndex[2] -= coordinateEps; } else { testCornerIndex[2] += coordinateEps; } if (bs.test(1)) { testCornerIndex[1] -= coordinateEps; } else { testCornerIndex[1] += coordinateEps; } if (bs.test(2)) { testCornerIndex[0] -= coordinateEps; } else { testCornerIndex[0] += coordinateEps; } isInside = referenceGeo.IsIndexInside(testCornerIndex); if (!isInside) { if (verbose) { MITK_INFO << "[( Geometry3D )] corner point is not inside. "; MITK_INFO << "referenceGeo is " << setprecision(12) << referenceGeo << " : tested corner is " << testGeo.GetCornerPoint(i); } result = false; } } // check grid of test geometry is on the grid of the reference geometry. This is important as the // boundingbox is only checked for containing the tested geometry, but if a corner (one is enough // as we know that axis and spacing are equal, due to equal transfor (see above)) of the tested geometry // is on the grid it is really a sub geometry (as they have the same spacing and axis). auto cornerOffset = testGeo.GetCornerPoint(0) - referenceGeo.GetCornerPoint(0); mitk::Vector3D cornerIndexOffset; referenceGeo.WorldToIndex(cornerOffset, cornerIndexOffset); for (unsigned int i = 0; i < 3; ++i) { auto pixelCountContinous = cornerIndexOffset[i]; auto pixelCount = std::round(pixelCountContinous); if (std::abs(pixelCount - pixelCountContinous) > coordinateEps) { if (verbose) { MITK_INFO << "[( Geometry3D )] Tested geometry is not on the grid of the reference geometry. "; MITK_INFO << "referenceGeo is " << setprecision(15) << referenceGeo << " : tested corner offset in pixels is " << pixelCountContinous << " for axis "< #include "mitkGroupTagProperty.h" #include "mitkProperties.h" #include "mitkSmartPointerProperty.h" #include "mitkStringProperty.h" //#include "mitkMaterialProperty.h" #include "mitkColorProperty.h" #include "mitkCoreObjectFactory.h" #include "mitkGenericProperty.h" #include "mitkGeometry3D.h" #include "mitkImageSource.h" #include "mitkLevelWindowProperty.h" #include "mitkRenderingManager.h" +namespace mitk +{ + itkEventMacroDefinition(InteractorChangedEvent, itk::AnyEvent); +} + mitk::Mapper *mitk::DataNode::GetMapper(MapperSlotId id) const { if ((id >= m_Mappers.size()) || (m_Mappers[id].IsNull())) { if (id >= m_Mappers.capacity()) { // int i, size=id-m_Mappers.capacity()+10; m_Mappers.resize(id + 10); } m_Mappers[id] = CoreObjectFactory::GetInstance()->CreateMapper(const_cast(this), id); } return m_Mappers[id]; } mitk::BaseData *mitk::DataNode::GetData() const { return m_Data; } void mitk::DataNode::SetData(mitk::BaseData *baseData) { if (m_Data != baseData) { m_Mappers.clear(); m_Mappers.resize(10); if (m_Data.IsNotNull() && baseData != nullptr) { // Do previous and new data have same type? Keep existing properties. if (0 == strcmp(m_Data->GetNameOfClass(), baseData->GetNameOfClass())) { m_Data = baseData; } else { m_Data = baseData; this->GetPropertyList()->Clear(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this); } } else { m_Data = baseData; mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(this); } m_DataReferenceChangedTime.Modified(); Modified(); } } mitk::DataNode::DataNode() : m_PropertyList(PropertyList::New()), m_PropertyListModifiedObserverTag(0) { m_Mappers.resize(10); // subscribe for modified event itk::MemberCommand::Pointer _PropertyListModifiedCommand = itk::MemberCommand::New(); _PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::DataNode::PropertyListModified); m_PropertyListModifiedObserverTag = m_PropertyList->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand); } mitk::DataNode::~DataNode() { if (m_PropertyList.IsNotNull()) m_PropertyList->RemoveObserver(m_PropertyListModifiedObserverTag); m_Mappers.clear(); m_Data = nullptr; } mitk::DataNode &mitk::DataNode::operator=(const DataNode &right) { mitk::DataNode *node = mitk::DataNode::New(); node->SetData(right.GetData()); return *node; } mitk::DataNode &mitk::DataNode::operator=(mitk::BaseData *right) { mitk::DataNode *node = mitk::DataNode::New(); node->SetData(right); return *node; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) std::istream &mitk::operator>>(std::istream &i, mitk::DataNode::Pointer &dtn) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) std::istream & operator>>(std::istream &i, mitk::DataNode::Pointer &dtn) #endif { dtn = mitk::DataNode::New(); // i >> av.get(); return i; } #if (_MSC_VER > 1200) || !defined(_MSC_VER) std::ostream &mitk::operator<<(std::ostream &o, mitk::DataNode::Pointer &dtn) #endif #if ((defined(_MSC_VER)) && (_MSC_VER <= 1200)) std::ostream & operator<<(std::ostream &o, mitk::DataNode::Pointer &dtn) #endif { if (dtn->GetData() != nullptr) o << dtn->GetData()->GetNameOfClass(); else o << "empty data"; return o; } void mitk::DataNode::SetMapper(MapperSlotId id, mitk::Mapper *mapper) { m_Mappers[id] = mapper; if (mapper != nullptr) mapper->SetDataNode(this); } void mitk::DataNode::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } } void mitk::DataNode::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::DataNode::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::DataNode::VerifyRequestedRegion() { return true; } void mitk::DataNode::SetRequestedRegion(const itk::DataObject * /*data*/) { } mitk::DataNode::PropertyListKeyNames mitk::DataNode::GetPropertyListNames() const { PropertyListKeyNames result; for (const auto &entries : m_MapOfPropertyLists) result.push_back(entries.first); return result; } void mitk::DataNode::CopyInformation(const itk::DataObject * /*data*/) { } mitk::PropertyList *mitk::DataNode::GetPropertyList(const mitk::BaseRenderer *renderer) const { if (renderer == nullptr) return m_PropertyList; return this->GetPropertyList(renderer->GetName()); } mitk::PropertyList *mitk::DataNode::GetPropertyList(const std::string &rendererName) const { if (rendererName.empty()) return m_PropertyList; mitk::PropertyList::Pointer &propertyList = m_MapOfPropertyLists[rendererName]; if (propertyList.IsNull()) propertyList = mitk::PropertyList::New(); assert(m_MapOfPropertyLists[rendererName].IsNotNull()); return propertyList; } void mitk::DataNode::ConcatenatePropertyList(PropertyList *pList, bool replace) { m_PropertyList->ConcatenatePropertyList(pList, replace); } mitk::BaseProperty *mitk::DataNode::GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer, bool fallBackOnDataProperties) const { if (nullptr == propertyKey) return nullptr; if (nullptr != renderer) { auto it = m_MapOfPropertyLists.find(renderer->GetName()); if (m_MapOfPropertyLists.end() != it) { auto property = it->second->GetProperty(propertyKey); if (nullptr != property) return property; } } auto property = m_PropertyList->GetProperty(propertyKey); if (nullptr == property && fallBackOnDataProperties && m_Data.IsNotNull()) property = m_Data->GetProperty(propertyKey); return property; } mitk::DataNode::GroupTagList mitk::DataNode::GetGroupTags() const { GroupTagList groups; const PropertyList::PropertyMap *propertyMap = m_PropertyList->GetMap(); for (auto groupIter = propertyMap->begin(); // m_PropertyList is created in the constructor, so we don't check it here groupIter != propertyMap->end(); ++groupIter) { const BaseProperty *bp = groupIter->second; if (dynamic_cast(bp)) { groups.insert(groupIter->first); } } return groups; } bool mitk::DataNode::GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer) const { mitk::BoolProperty::Pointer boolprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (boolprop.IsNull()) return false; boolValue = boolprop->GetValue(); return true; } bool mitk::DataNode::GetIntProperty(const char *propertyKey, int &intValue, const mitk::BaseRenderer *renderer) const { mitk::IntProperty::Pointer intprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (intprop.IsNull()) return false; intValue = intprop->GetValue(); return true; } bool mitk::DataNode::GetFloatProperty(const char *propertyKey, float &floatValue, const mitk::BaseRenderer *renderer) const { mitk::FloatProperty::Pointer floatprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (floatprop.IsNull()) return false; floatValue = floatprop->GetValue(); return true; } bool mitk::DataNode::GetDoubleProperty(const char *propertyKey, double &doubleValue, const mitk::BaseRenderer *renderer) const { mitk::DoubleProperty::Pointer doubleprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (doubleprop.IsNull()) { // try float instead float floatValue = 0; if (this->GetFloatProperty(propertyKey, floatValue, renderer)) { doubleValue = floatValue; return true; } return false; } doubleValue = doubleprop->GetValue(); return true; } bool mitk::DataNode::GetStringProperty(const char *propertyKey, std::string &string, const mitk::BaseRenderer *renderer) const { mitk::StringProperty::Pointer stringProp = dynamic_cast(GetProperty(propertyKey, renderer)); if (stringProp.IsNull()) { return false; } else { // memcpy((void*)string, stringProp->GetValue(), strlen(stringProp->GetValue()) + 1 ); // looks dangerous string = stringProp->GetValue(); return true; } } bool mitk::DataNode::GetColor(float rgb[3], const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (colorprop.IsNull()) return false; memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); return true; } bool mitk::DataNode::GetOpacity(float &opacity, const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::FloatProperty::Pointer opacityprop = dynamic_cast(GetProperty(propertyKey, renderer)); if (opacityprop.IsNull()) return false; opacity = opacityprop->GetValue(); return true; } bool mitk::DataNode::GetLevelWindow(mitk::LevelWindow &levelWindow, const mitk::BaseRenderer *renderer, const char *propertyKey) const { mitk::LevelWindowProperty::Pointer levWinProp = dynamic_cast(GetProperty(propertyKey, renderer)); if (levWinProp.IsNull()) return false; levelWindow = levWinProp->GetLevelWindow(); return true; } void mitk::DataNode::SetColor(const mitk::Color &color, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(color); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetColor( float red, float green, float blue, const mitk::BaseRenderer *renderer, const char *propertyKey) { float color[3]; color[0] = red; color[1] = green; color[2] = blue; SetColor(color, renderer, propertyKey); } void mitk::DataNode::SetColor(const float rgb[3], const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(rgb); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetVisibility(bool visible, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::BoolProperty::Pointer prop; prop = mitk::BoolProperty::New(visible); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetOpacity(float opacity, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::FloatProperty::Pointer prop; prop = mitk::FloatProperty::New(opacity); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetLevelWindow(mitk::LevelWindow levelWindow, const mitk::BaseRenderer *renderer, const char *propertyKey) { mitk::LevelWindowProperty::Pointer prop; prop = mitk::LevelWindowProperty::New(levelWindow); GetPropertyList(renderer)->SetProperty(propertyKey, prop); } void mitk::DataNode::SetIntProperty(const char *propertyKey, int intValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::DataNode::SetBoolProperty(const char *propertyKey, bool boolValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::DataNode::SetFloatProperty(const char *propertyKey, float floatValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { if (dynamic_cast(this->GetProperty(propertyKey, renderer)) != nullptr) { MITK_WARN << "Setting float property " << propertyKey << " although a double property with the same name already exists"; } GetPropertyList(renderer)->SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::DataNode::SetDoubleProperty(const char *propertyKey, double doubleValue, const mitk::BaseRenderer *renderer) { if (dynamic_cast(this->GetProperty(propertyKey, renderer)) != nullptr) { MITK_WARN << "Setting double property " << propertyKey << " although a float property with the same name already exists"; } GetPropertyList(renderer)->SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); } void mitk::DataNode::SetStringProperty(const char *propertyKey, const char *stringValue, const mitk::BaseRenderer *renderer /*=nullptr*/) { GetPropertyList(renderer)->SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::DataNode::SetProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->SetProperty(propertyKey, propertyValue); } void mitk::DataNode::ReplaceProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer) { GetPropertyList(renderer)->ReplaceProperty(propertyKey, propertyValue); } void mitk::DataNode::AddProperty(const char *propertyKey, BaseProperty *propertyValue, const mitk::BaseRenderer *renderer, bool overwrite) { if ((overwrite) || (GetProperty(propertyKey, renderer) == nullptr)) { SetProperty(propertyKey, propertyValue, renderer); } } vtkLinearTransform *mitk::DataNode::GetVtkTransform(int t) const { assert(m_Data.IsNotNull()); mitk::BaseGeometry *geometry = m_Data->GetGeometry(t); if (geometry == nullptr) return nullptr; return geometry->GetVtkTransform(); } -unsigned long mitk::DataNode::GetMTime() const +itk::ModifiedTimeType mitk::DataNode::GetMTime() const { - unsigned long time = Superclass::GetMTime(); + auto time = Superclass::GetMTime(); if (m_Data.IsNotNull()) { if ((time < m_Data->GetMTime()) || ((m_Data->GetSource().IsNotNull()) && (time < m_Data->GetSource()->GetMTime()))) { Modified(); return Superclass::GetMTime(); } } return time; } void mitk::DataNode::SetSelected(bool selected, const mitk::BaseRenderer *renderer) { mitk::BoolProperty::Pointer selectedProperty = dynamic_cast(GetProperty("selected")); if (selectedProperty.IsNull()) { selectedProperty = mitk::BoolProperty::New(); selectedProperty->SetValue(false); SetProperty("selected", selectedProperty, renderer); } if (selectedProperty->GetValue() != selected) { selectedProperty->SetValue(selected); itk::ModifiedEvent event; InvokeEvent(event); } } /* class SelectedEvent : public itk::ModifiedEvent { public: typedef SelectedEvent Self; typedef itk::ModifiedEvent Superclass; SelectedEvent(DataNode* dataNode) { m_DataNode = dataNode; }; DataNode* GetDataNode() { return m_DataNode; }; virtual const char * GetEventName() const { return "SelectedEvent"; } virtual bool CheckEvent(const ::itk::EventObject* e) const { return dynamic_cast(e); } virtual ::itk::EventObject* MakeObject() const { return new Self(m_DataNode); } private: DataNode* m_DataNode; SelectedEvent(const Self& event) { m_DataNode = event.m_DataNode; }; void operator=(const Self& event) { m_DataNode = event.m_DataNode; } }; */ bool mitk::DataNode::IsSelected(const mitk::BaseRenderer *renderer) { bool selected; if (!GetBoolProperty("selected", selected, renderer)) return false; return selected; } void mitk::DataNode::SetDataInteractor(const DataInteractor::Pointer interactor) { if (m_DataInteractor == interactor) return; m_DataInteractor = interactor; this->Modified(); - mitk::DataNode::InteractorChangedEvent changedEvent; + InteractorChangedEvent changedEvent; this->InvokeEvent(changedEvent); } mitk::DataInteractor::Pointer mitk::DataNode::GetDataInteractor() const { return m_DataInteractor; } void mitk::DataNode::PropertyListModified(const itk::Object * /*caller*/, const itk::EventObject &) { Modified(); } mitk::BaseProperty::ConstPointer mitk::DataNode::GetConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) const { if (propertyKey.empty()) return nullptr; if (!contextName.empty()) { auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { BaseProperty::ConstPointer property = propertyListIter->second->GetProperty(propertyKey); if (property.IsNotNull()) return property; } } if (contextName.empty() || fallBackOnDefaultContext) { BaseProperty::ConstPointer property = m_PropertyList->GetProperty(propertyKey); if (property.IsNull() && m_Data.IsNotNull()) property = m_Data->GetProperty(propertyKey.c_str()); return property; } return nullptr; } mitk::BaseProperty * mitk::DataNode::GetNonConstProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) return nullptr; if (!contextName.empty()) { auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { auto property = propertyListIter->second->GetProperty(propertyKey); if (nullptr != property) return property; } } if (contextName.empty() || fallBackOnDefaultContext) { auto property = m_PropertyList->GetProperty(propertyKey); if (nullptr == property && m_Data.IsNotNull()) property = m_Data->GetProperty(propertyKey.c_str()); return property; } return nullptr; } void mitk::DataNode::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (!contextName.empty()) { auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { propertyListIter->second->SetProperty(propertyKey, property); return; } } if (contextName.empty() || fallBackOnDefaultContext) { m_PropertyList->SetProperty(propertyKey, property); return; } mitkThrow() << "Unknown property context."; } void mitk::DataNode::RemoveProperty(const std::string &propertyKey, const std::string &contextName, bool fallBackOnDefaultContext) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (!contextName.empty()) { auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { propertyListIter->second->RemoveProperty(propertyKey); return; } } if (contextName.empty() || fallBackOnDefaultContext) { m_PropertyList->RemoveProperty(propertyKey); return; } mitkThrow() << "Unknown property context."; } std::vector mitk::DataNode::GetPropertyKeys(const std::string &contextName, bool includeDefaultContext) const { std::vector propertyKeys; if (contextName.empty()) { for (const auto &property : *m_PropertyList->GetMap()) propertyKeys.push_back(property.first); return propertyKeys; } auto propertyListIter = m_MapOfPropertyLists.find(contextName); if (m_MapOfPropertyLists.end() != propertyListIter) { for (const auto &property : *propertyListIter->second->GetMap()) propertyKeys.push_back(property.first); } if (includeDefaultContext) { for (const auto &property : *m_PropertyList->GetMap()) { auto propertyKeyIter = std::find(propertyKeys.begin(), propertyKeys.end(), property.first); if (propertyKeys.end() == propertyKeyIter) propertyKeys.push_back(property.first); } } return propertyKeys; } std::vector mitk::DataNode::GetPropertyContextNames() const { return this->GetPropertyListNames(); } diff --git a/Modules/Core/src/DataManagement/mitkDataStorage.cpp b/Modules/Core/src/DataManagement/mitkDataStorage.cpp index 5a555a15da..894407d29e 100644 --- a/Modules/Core/src/DataManagement/mitkDataStorage.cpp +++ b/Modules/Core/src/DataManagement/mitkDataStorage.cpp @@ -1,584 +1,583 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkDataStorage.h" #include "itkCommand.h" -#include "itkMutexLockHolder.h" #include "mitkDataNode.h" #include "mitkGroupTagProperty.h" #include "mitkImage.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "mitkArbitraryTimeGeometry.h" mitk::DataStorage::DataStorage() : itk::Object(), m_BlockNodeModifiedEvents(false) { } mitk::DataStorage::~DataStorage() { ///// we can not call GetAll() in destructor, because it is implemented in a subclass // SetOfObjects::ConstPointer all = this->GetAll(); // for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) // this->RemoveListeners(it->Value()); // m_NodeModifiedObserverTags.clear(); // m_NodeDeleteObserverTags.clear(); } void mitk::DataStorage::Add(DataNode *node, DataNode *parent) { DataStorage::SetOfObjects::Pointer parents = DataStorage::SetOfObjects::New(); if (parent != nullptr) //< Return empty set if parent is null parents->InsertElement(0, parent); this->Add(node, parents); } void mitk::DataStorage::Remove(const DataStorage::SetOfObjects *nodes) { if (nodes == nullptr) return; for (DataStorage::SetOfObjects::ConstIterator it = nodes->Begin(); it != nodes->End(); it++) this->Remove(it.Value()); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::GetSubset(const NodePredicateBase *condition) const { DataStorage::SetOfObjects::ConstPointer result = this->FilterSetOfObjects(this->GetAll(), condition); return result; } mitk::DataNode *mitk::DataStorage::GetNamedNode(const char *name) const { if (name == nullptr) return nullptr; StringProperty::Pointer s(StringProperty::New(name)); NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s); DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(p); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNode(const NodePredicateBase *condition) const { if (condition == nullptr) return nullptr; DataStorage::SetOfObjects::ConstPointer rs = this->GetSubset(condition); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } mitk::DataNode *mitk::DataStorage::GetNamedDerivedNode(const char *name, const DataNode *sourceNode, bool onlyDirectDerivations) const { if (name == nullptr) return nullptr; StringProperty::Pointer s(StringProperty::New(name)); NodePredicateProperty::Pointer p = NodePredicateProperty::New("name", s); DataStorage::SetOfObjects::ConstPointer rs = this->GetDerivations(sourceNode, p, onlyDirectDerivations); if (rs->Size() >= 1) return rs->GetElement(0); else return nullptr; } void mitk::DataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const { // Superclass::PrintSelf(os, indent); DataStorage::SetOfObjects::ConstPointer all = this->GetAll(); os << indent << "DataStorage " << this << " is managing " << all->Size() << " objects. List of objects:" << std::endl; for (DataStorage::SetOfObjects::ConstIterator allIt = all->Begin(); allIt != all->End(); allIt++) { std::string name; allIt.Value()->GetName(name); std::string datatype; if (allIt.Value()->GetData() != nullptr) datatype = allIt.Value()->GetData()->GetNameOfClass(); os << indent << " " << allIt.Value().GetPointer() << "<" << datatype << ">: " << name << std::endl; DataStorage::SetOfObjects::ConstPointer parents = this->GetSources(allIt.Value()); if (parents->Size() > 0) { os << indent << " Direct sources: "; for (DataStorage::SetOfObjects::ConstIterator parentIt = parents->Begin(); parentIt != parents->End(); parentIt++) os << parentIt.Value().GetPointer() << ", "; os << std::endl; } DataStorage::SetOfObjects::ConstPointer derivations = this->GetDerivations(allIt.Value()); if (derivations->Size() > 0) { os << indent << " Direct derivations: "; for (DataStorage::SetOfObjects::ConstIterator derivationIt = derivations->Begin(); derivationIt != derivations->End(); derivationIt++) os << derivationIt.Value().GetPointer() << ", "; os << std::endl; } } os << std::endl; } mitk::DataStorage::SetOfObjects::ConstPointer mitk::DataStorage::FilterSetOfObjects(const SetOfObjects *set, const NodePredicateBase *condition) const { if (set == nullptr) return nullptr; DataStorage::SetOfObjects::Pointer result = DataStorage::SetOfObjects::New(); for (DataStorage::SetOfObjects::ConstIterator it = set->Begin(); it != set->End(); it++) if (condition == nullptr || condition->CheckNode(it.Value()) == true) // alway copy the set, otherwise the iterator in DataStorage::Remove() will crash result->InsertElement(result->Size(), it.Value()); return DataStorage::SetOfObjects::ConstPointer(result); } const mitk::DataNode::GroupTagList mitk::DataStorage::GetGroupTags() const { DataNode::GroupTagList result; SetOfObjects::ConstPointer all = this->GetAll(); if (all.IsNull()) return result; for (DataStorage::SetOfObjects::ConstIterator nodeIt = all->Begin(); nodeIt != all->End(); nodeIt++) // for each node { PropertyList *pl = nodeIt.Value()->GetPropertyList(); for (auto propIt = pl->GetMap()->begin(); propIt != pl->GetMap()->end(); ++propIt) if (dynamic_cast(propIt->second.GetPointer()) != nullptr) result.insert(propIt->first); } return result; } void mitk::DataStorage::EmitAddNodeEvent(const DataNode *node) { AddNodeEvent.Send(node); } void mitk::DataStorage::EmitRemoveNodeEvent(const DataNode *node) { RemoveNodeEvent.Send(node); } void mitk::DataStorage::OnNodeInteractorChanged(itk::Object *caller, const itk::EventObject &) { const auto *_Node = dynamic_cast(caller); if (_Node) { InteractorChangedNodeEvent.Send(_Node); } } void mitk::DataStorage::OnNodeModifiedOrDeleted(const itk::Object *caller, const itk::EventObject &event) { if (m_BlockNodeModifiedEvents) return; const auto *_Node = dynamic_cast(caller); if (_Node) { const auto *modEvent = dynamic_cast(&event); if (modEvent) ChangedNodeEvent.Send(_Node); else DeleteNodeEvent.Send(_Node); } } void mitk::DataStorage::AddListeners(const DataNode *_Node) { - itk::MutexLockHolder locked(m_MutexOne); + std::lock_guard locked(m_MutexOne); // node must not be 0 and must not be yet registered auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) == m_NodeModifiedObserverTags.end()) { itk::MemberCommand::Pointer nodeModifiedCommand = itk::MemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted); m_NodeModifiedObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); itk::MemberCommand::Pointer interactorChangedCommand = itk::MemberCommand::New(); interactorChangedCommand->SetCallbackFunction(this, &DataStorage::OnNodeInteractorChanged); m_NodeInteractorChangedObserverTags[NonConstNode] = - NonConstNode->AddObserver(DataNode::InteractorChangedEvent(), interactorChangedCommand); + NonConstNode->AddObserver(InteractorChangedEvent(), interactorChangedCommand); // add itk delete listener on datastorage itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction(this, &DataStorage::OnNodeModifiedOrDeleted); // add observer m_NodeDeleteObserverTags[NonConstNode] = NonConstNode->AddObserver(itk::DeleteEvent(), deleteCommand); } } void mitk::DataStorage::RemoveListeners(const DataNode *_Node) { - itk::MutexLockHolder locked(m_MutexOne); + std::lock_guard locked(m_MutexOne); // node must not be 0 and must be registered auto *NonConstNode = const_cast(_Node); if (_Node && m_NodeModifiedObserverTags.find(NonConstNode) != m_NodeModifiedObserverTags.end()) { // const cast is bad! but sometimes it is necessary. removing an observer does not really // touch the internal state NonConstNode->RemoveObserver(m_NodeModifiedObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeDeleteObserverTags.find(NonConstNode)->second); NonConstNode->RemoveObserver(m_NodeInteractorChangedObserverTags.find(NonConstNode)->second); m_NodeModifiedObserverTags.erase(NonConstNode); m_NodeDeleteObserverTags.erase(NonConstNode); m_NodeInteractorChangedObserverTags.erase(NonConstNode); } } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const SetOfObjects *input, const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) const { if (input == nullptr) throw std::invalid_argument("DataStorage: input is invalid"); BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; Vector3D minSpacing; minSpacing.Fill(itk::NumericTraits::max()); ScalarType stmax = itk::NumericTraits::max(); ScalarType stmin = itk::NumericTraits::NonpositiveMin(); std::set existingTimePoints; ScalarType maximalTime = 0; // Needed for check of zero bounding boxes ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); for (SetOfObjects::ConstIterator it = input->Begin(); it != input->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *timeGeometry = node->GetData()->GetUpdatedTimeGeometry(); if (timeGeometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = timeGeometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = timeGeometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } try { // time bounds // iterate over all time steps // Attention: Objects with zero bounding box are not respected in time bound calculation for (TimeStepType i = 0; i < timeGeometry->CountTimeSteps(); i++) { // We must not use 'node->GetData()->GetGeometry(i)->GetSpacing()' here, as it returns the spacing // in its original space, which, in case of an image geometry, can have the values in different // order than in world space. For the further calculations, we need to have the spacing values // in world coordinate order (sag-cor-ax). Vector3D spacing; spacing.Fill(1.0); node->GetData()->GetGeometry(i)->IndexToWorld(spacing, spacing); for (int axis = 0; axis < 3; ++ axis) { ScalarType space = std::abs(spacing[axis]); if (space < minSpacing[axis]) { minSpacing[axis] = space; } } const auto curTimeBounds = timeGeometry->GetTimeBounds(i); if ((curTimeBounds[0] > stmin) && (curTimeBounds[0] < stmax)) { existingTimePoints.insert(curTimeBounds[0]); } if ((curTimeBounds[1] > maximalTime) && (curTimeBounds[1] < stmax)) { maximalTime = curTimeBounds[1]; } } } catch ( const itk::ExceptionObject &e ) { MITK_ERROR << e.GetDescription() << std::endl; } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); // compute the number of time steps if (existingTimePoints.empty()) // make sure that there is at least one time sliced geometry in the data storage { existingTimePoints.insert(0.0); maximalTime = 1.0; } ArbitraryTimeGeometry::Pointer timeGeometry = nullptr; if (result->GetPoints()->Size() > 0) { // Initialize a geometry of a single time step Geometry3D::Pointer geometry = Geometry3D::New(); geometry->Initialize(); // correct bounding-box (is now in mm, should be in index-coordinates) // according to spacing BoundingBox::BoundsArrayType bounds = result->GetBounds(); AffineTransform3D::OutputVectorType offset; for (int i = 0; i < 3; ++i) { offset[i] = bounds[i * 2]; bounds[i * 2] = 0.0; bounds[i * 2 + 1] = (bounds[i * 2 + 1] - offset[i]) / minSpacing[i]; } geometry->GetIndexToWorldTransform()->SetOffset(offset); geometry->SetBounds(bounds); geometry->SetSpacing(minSpacing); // Initialize the time sliced geometry auto tsIterator = existingTimePoints.cbegin(); auto tsPredecessor = tsIterator++; auto tsEnd = existingTimePoints.cend(); timeGeometry = ArbitraryTimeGeometry::New(); for (; tsIterator != tsEnd; ++tsIterator, ++tsPredecessor) { timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, *tsIterator); } timeGeometry->AppendNewTimeStep(geometry, *tsPredecessor, maximalTime); timeGeometry->Update(); } return timeGeometry.GetPointer(); } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeBoundingGeometry3D(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) const { return this->ComputeBoundingGeometry3D(this->GetAll(), boolPropertyKey, renderer, boolPropertyKey2); } mitk::TimeGeometry::ConstPointer mitk::DataStorage::ComputeVisibleBoundingGeometry3D(const BaseRenderer *renderer, const char *boolPropertyKey) { return ComputeBoundingGeometry3D("visible", renderer, boolPropertyKey); } mitk::BoundingBox::Pointer mitk::DataStorage::ComputeBoundingBox(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) { BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); BoundingBox::PointIdentifier pointid = 0; Point3D point; // Needed for check of zero bounding boxes ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsZero(nullpoint); SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { // bounding box (only if non-zero) BoundingBox::BoundsArrayType itkBounds = geometry->GetBoundingBoxInWorld()->GetBounds(); if (itkBounds == itkBoundsZero) { continue; } unsigned char i; for (i = 0; i < 8; ++i) { point = geometry->GetCornerPointInWorld(i); if (point[0] * point[0] + point[1] * point[1] + point[2] * point[2] < large) pointscontainer->InsertElement(pointid++, point); else { itkGenericOutputMacro(<< "Unrealistically distant corner point encountered. Ignored. Node: " << node); } } } } } BoundingBox::Pointer result = BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } mitk::TimeBounds mitk::DataStorage::ComputeTimeBounds(const char *boolPropertyKey, const BaseRenderer *renderer, const char *boolPropertyKey2) { TimeBounds timeBounds; ScalarType stmin, stmax, cur; stmin = itk::NumericTraits::NonpositiveMin(); stmax = itk::NumericTraits::max(); timeBounds[0] = stmax; timeBounds[1] = stmin; SetOfObjects::ConstPointer all = this->GetAll(); for (SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { DataNode::Pointer node = it->Value(); if ((node.IsNotNull()) && (node->GetData() != nullptr) && (node->GetData()->IsEmpty() == false) && node->IsOn(boolPropertyKey, renderer) && node->IsOn(boolPropertyKey2, renderer)) { const TimeGeometry *geometry = node->GetData()->GetUpdatedTimeGeometry(); if (geometry != nullptr) { const TimeBounds &curTimeBounds = geometry->GetTimeBounds(); cur = curTimeBounds[0]; // is it after -infinity, but before everything else that we found until now? if ((cur > stmin) && (cur < timeBounds[0])) timeBounds[0] = cur; cur = curTimeBounds[1]; // is it before infinity, but after everything else that we found until now? if ((cur < stmax) && (cur > timeBounds[1])) timeBounds[1] = cur; } } } if (!(timeBounds[0] < stmax)) { timeBounds[0] = stmin; timeBounds[1] = stmax; } return timeBounds; } void mitk::DataStorage::BlockNodeModifiedEvents(bool block) { m_BlockNodeModifiedEvents = block; } mitk::DataNode::Pointer mitk::FindTopmostVisibleNode(const DataStorage::SetOfObjects::ConstPointer nodes, const Point3D worldPosition, const TimePointType timePoint, const BaseRenderer* baseRender) { if (nodes.IsNull()) { return nullptr; } mitk::DataNode::Pointer topLayerNode = nullptr; int maxLayer = std::numeric_limits::min(); for (const auto &node : *nodes) { if (node.IsNull()) { continue; } bool isHelperObject = false; node->GetBoolProperty("helper object", isHelperObject); if (isHelperObject) { continue; } auto data = node->GetData(); if (nullptr == data) { continue; } auto geometry = data->GetGeometry(); if (nullptr == geometry || !geometry->IsInside(worldPosition)) { continue; } auto timeGeometry = data->GetUpdatedTimeGeometry(); if (nullptr == timeGeometry) { continue; } if (!timeGeometry->IsValidTimePoint(timePoint)) { continue; } int layer = 0; if (!node->GetIntProperty("layer", layer, baseRender)) { continue; } if (layer <= maxLayer) { continue; } if (!node->IsVisible(baseRender)) { continue; } topLayerNode = node; maxLayer = layer; } return topLayerNode; } diff --git a/Modules/Core/src/DataManagement/mitkGeometryTransformHolder.cpp b/Modules/Core/src/DataManagement/mitkGeometryTransformHolder.cpp index a20204ef84..3ee34c7cf6 100644 --- a/Modules/Core/src/DataManagement/mitkGeometryTransformHolder.cpp +++ b/Modules/Core/src/DataManagement/mitkGeometryTransformHolder.cpp @@ -1,335 +1,335 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ /* * mitkGeometryTransformHolder.cpp * * Created on: Sep 3, 2014 * Author: wirkert */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { void GeometryTransformHolder::CopySpacingFromTransform(const mitk::AffineTransform3D *transform, mitk::Vector3D &spacing) { mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = transform->GetMatrix().GetVnlMatrix(); spacing[0] = vnlmatrix.get_column(0).magnitude(); spacing[1] = vnlmatrix.get_column(1).magnitude(); spacing[2] = vnlmatrix.get_column(2).magnitude(); } GeometryTransformHolder::GeometryTransformHolder() { m_VtkMatrix = vtkMatrix4x4::New(); m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); this->Initialize(); } GeometryTransformHolder::GeometryTransformHolder(const GeometryTransformHolder &other) { m_VtkMatrix = vtkMatrix4x4::New(); m_VtkIndexToWorldTransform = vtkMatrixToLinearTransform::New(); m_VtkIndexToWorldTransform->SetInput(m_VtkMatrix); this->Initialize(&other); } GeometryTransformHolder::~GeometryTransformHolder() { m_VtkMatrix->Delete(); m_VtkIndexToWorldTransform->Delete(); } void GeometryTransformHolder::Initialize() { if (m_IndexToWorldTransform.IsNull()) m_IndexToWorldTransform = TransformType::New(); else m_IndexToWorldTransform->SetIdentity(); m_VtkMatrix->Identity(); } void GeometryTransformHolder::Initialize(const GeometryTransformHolder *other) { Initialize(); if (other->GetIndexToWorldTransform()) { TransformType::Pointer indexToWorldTransform = other->GetIndexToWorldTransform()->Clone(); this->SetIndexToWorldTransform(indexToWorldTransform); } } //##Documentation //## @brief Copy the ITK transform //## (m_IndexToWorldTransform) to the VTK transform //## \sa SetIndexToWorldTransform void GeometryTransformHolder::TransferItkToVtkTransform() { TransferItkTransformToVtkMatrix(m_IndexToWorldTransform.GetPointer(), m_VtkMatrix); } //##Documentation //## @brief Copy the VTK transform //## to the ITK transform (m_IndexToWorldTransform) //## \sa SetIndexToWorldTransform void GeometryTransformHolder::TransferVtkToItkTransform() { TransferVtkMatrixToItkTransform(m_VtkMatrix, m_IndexToWorldTransform.GetPointer()); } //##Documentation //## @brief Get the origin, e.g. the upper-left corner of the plane const Point3D GeometryTransformHolder::GetOrigin() const { Point3D origin = Point3D(this->GetIndexToWorldTransform()->GetOffset()); return origin; } //##Documentation //## @brief Set the origin, i.e. the upper-left corner of the plane //## void GeometryTransformHolder::SetOrigin(const Point3D &origin) { m_IndexToWorldTransform->SetOffset(origin.GetVectorFromOrigin()); TransferItkToVtkTransform(); } //##Documentation //## @brief Get the spacing (size of a pixel). //## const mitk::Vector3D GeometryTransformHolder::GetSpacing() const { mitk::Vector3D spacing; CopySpacingFromTransform(this->GetIndexToWorldTransform(), spacing); return spacing; } //##Documentation //## @brief Set the spacing. //## //##The spacing is also changed in the IndexToWorldTransform. void GeometryTransformHolder::SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { if (mitk::Equal(this->GetSpacing(), aSpacing) == false || enforceSetSpacing) { assert(aSpacing[0] > 0 && aSpacing[1] > 0 && aSpacing[2] > 0); AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); mitk::VnlVector col; - col = vnlmatrix.get_column(0); + col = vnlmatrix.get_column(0).as_ref(); col.normalize(); col *= aSpacing[0]; vnlmatrix.set_column(0, col); - col = vnlmatrix.get_column(1); + col = vnlmatrix.get_column(1).as_ref(); col.normalize(); col *= aSpacing[1]; vnlmatrix.set_column(1, col); - col = vnlmatrix.get_column(2); + col = vnlmatrix.get_column(2).as_ref(); col.normalize(); col *= aSpacing[2]; vnlmatrix.set_column(2, col); Matrix3D matrix; matrix = vnlmatrix; AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(matrix); transform->SetOffset(m_IndexToWorldTransform->GetOffset()); SetIndexToWorldTransform(transform.GetPointer()); } } //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates mitk::AffineTransform3D *GeometryTransformHolder::GetIndexToWorldTransform() { return m_IndexToWorldTransform; } //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates const mitk::AffineTransform3D *GeometryTransformHolder::GetIndexToWorldTransform() const { return m_IndexToWorldTransform; } //## @brief Set the transformation used to convert from index //## to world coordinates. The spacing of the new transform is //## copied to m_spacing. void GeometryTransformHolder::SetIndexToWorldTransform(mitk::AffineTransform3D *transform) { m_IndexToWorldTransform = transform; TransferItkToVtkTransform(); } //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4.The spacing of //## the new transform is copied to m_spacing. //## \sa SetIndexToWorldTransform void GeometryTransformHolder::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix) { m_VtkMatrix->DeepCopy(vtkmatrix); TransferVtkToItkTransform(); } //## @brief Set the transformation used to convert from index //## to world coordinates.This function keeps the original spacing. void GeometryTransformHolder::SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform) { mitk::Vector3D originalSpacing = this->GetSpacing(); this->SetIndexToWorldTransform(transform); this->SetSpacing(originalSpacing); } //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4. This function keeps the original spacing. //## \sa SetIndexToWorldTransform void GeometryTransformHolder::SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix) { mitk::Vector3D originalSpacing = this->GetSpacing(); this->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); this->SetSpacing(originalSpacing); } //## Get the Vtk Matrix which describes the transform. vtkMatrix4x4 *GeometryTransformHolder::GetVtkMatrix() { return m_VtkMatrix; } //## Get the Vtk Matrix which describes the transform. const vtkMatrix4x4 *GeometryTransformHolder::GetVtkMatrix() const { return m_VtkMatrix; } //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform vtkLinearTransform *GeometryTransformHolder::GetVtkTransform() const { return (vtkLinearTransform *)m_VtkIndexToWorldTransform; } void GeometryTransformHolder::SetMatrix(Matrix3D &matrix) { m_IndexToWorldTransform->SetMatrix(matrix); TransferItkToVtkTransform(); } void GeometryTransformHolder::SetIdentity() { m_IndexToWorldTransform->SetIdentity(); TransferItkToVtkTransform(); } void GeometryTransformHolder::Compose(const TransformType *other, bool pre) { m_IndexToWorldTransform->Compose(other, pre); TransferItkToVtkTransform(); } void GeometryTransformHolder::SetVtkMatrixDeepCopy(vtkTransform *vtktransform) { m_VtkMatrix->DeepCopy(vtktransform->GetMatrix()); TransferVtkToItkTransform(); } bool GeometryTransformHolder::IsIndexToWorldTransformNull() { return m_IndexToWorldTransform.IsNull(); } AffineTransform3D::MatrixType::InternalMatrixType GeometryTransformHolder::GetVnlMatrix() { return m_IndexToWorldTransform->GetMatrix().GetVnlMatrix(); } } bool mitk::Equal(const mitk::GeometryTransformHolder *leftHandSide, const mitk::GeometryTransformHolder *rightHandSide, ScalarType eps, bool verbose) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal(const mitk::Geometry3D *leftHandSide, const mitk::Geometry3D *rightHandSide, ScalarType " "eps, bool verbose) does not with nullptr pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::GeometryTransformHolder &leftHandSide, const mitk::GeometryTransformHolder &rightHandSide, ScalarType eps, bool verbose) { bool result = true; // Compare spacings if (!mitk::Equal(leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is " << leftHandSide.GetSpacing() << " and tolerance is " << eps; } result = false; } // Compare Origins if (!mitk::Equal(leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Origin differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is " << leftHandSide.GetOrigin() << " and tolerance is " << eps; } result = false; } // Compare IndexToWorldTransform Matrix if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), eps, verbose)) { result = false; } // Compare vtk Matrix for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { if (leftHandSide.GetVtkMatrix()->GetElement(i, j) != rightHandSide.GetVtkMatrix()->GetElement(i, j)) { result = false; } } } return result; } diff --git a/Modules/Core/src/DataManagement/mitkImage.cpp b/Modules/Core/src/DataManagement/mitkImage.cpp index bd5ba5e95d..0c10c1302a 100644 --- a/Modules/Core/src/DataManagement/mitkImage.cpp +++ b/Modules/Core/src/DataManagement/mitkImage.cpp @@ -1,1358 +1,1355 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkImage.h" #include "mitkCompareImageDataFilter.h" #include "mitkImageStatisticsHolder.h" #include "mitkImageVtkReadAccessor.h" #include "mitkImageVtkWriteAccessor.h" #include "mitkPixelTypeMultiplex.h" #include // VTK #include -// ITK -#include - // Other #include #define FILL_C_ARRAY(_arr, _size, _value) \ for (unsigned int i = 0u; i < _size; i++) \ \ { \ _arr[i] = _value; \ } mitk::Image::Image() : m_Dimension(0), m_Dimensions(nullptr), m_ImageDescriptor(nullptr), m_OffsetTable(nullptr), m_CompleteData(nullptr), m_ImageStatistics(nullptr) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY(m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); m_Initialized = false; } mitk::Image::Image(const Image &other) : SlicedData(other), m_Dimension(0), m_Dimensions(nullptr), m_ImageDescriptor(nullptr), m_OffsetTable(nullptr), m_CompleteData(nullptr), m_ImageStatistics(nullptr) { m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; FILL_C_ARRAY(m_Dimensions, MAX_IMAGE_DIMENSIONS, 0u); this->Initialize(other.GetPixelType(), other.GetDimension(), other.GetDimensions()); // Since the above called "Initialize" method doesn't take the geometry into account we need to set it // here manually TimeGeometry::Pointer cloned = other.GetTimeGeometry()->Clone(); this->SetTimeGeometry(cloned.GetPointer()); if (this->GetDimension() > 3) { const unsigned int time_steps = this->GetDimension(3); for (unsigned int i = 0u; i < time_steps; ++i) { ImageDataItemPointer volume = other.GetVolumeData(i); this->SetVolume(volume->GetData(), i); } } else { ImageDataItemPointer volume = other.GetVolumeData(0); this->SetVolume(volume->GetData(), 0); } } mitk::Image::~Image() { this->Clear(); m_ReferenceCount = 3; m_ReferenceCount = 0; delete[] m_OffsetTable; delete m_ImageStatistics; } const mitk::PixelType mitk::Image::GetPixelType(int n) const { return this->m_ImageDescriptor->GetChannelTypeById(n); } unsigned int mitk::Image::GetDimension() const { return m_Dimension; } unsigned int mitk::Image::GetDimension(int i) const { if ((i >= 0) && (i < (int)m_Dimension)) return m_Dimensions[i]; return 1; } template void AccessPixel(const mitk::PixelType ptype, void *data, const unsigned int offset, double &value) { value = 0.0; if (data == nullptr) return; if (ptype.GetBpe() != 24) { value = (double)(((T *)data)[offset]); } else { const unsigned int rgboffset = offset; double returnvalue = (((T *)data)[rgboffset]); returnvalue += (((T *)data)[rgboffset + 1]); returnvalue += (((T *)data)[rgboffset + 2]); value = returnvalue; } } vtkImageData *mitk::Image::GetVtkImageData(int t, int n) { if (m_Initialized == false) { if (GetSource().IsNull()) return nullptr; if (GetSource()->Updating() == false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume = GetVolumeData(t, n); return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData(); } const vtkImageData *mitk::Image::GetVtkImageData(int t, int n) const { if (m_Initialized == false) { if (GetSource().IsNull()) return nullptr; if (GetSource()->Updating() == false) GetSource()->UpdateOutputInformation(); } ImageDataItemPointer volume = GetVolumeData(t, n); return volume.GetPointer() == nullptr ? nullptr : volume->GetVtkImageAccessor(this)->GetVtkImageData(); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetSliceData_unlocked(s, t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidSlice(s, t, n) == false) return nullptr; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // slice directly available? int pos = GetSliceIndex(s, t, n); if (m_Slices[pos].GetPointer() != nullptr) { return m_Slices[pos]; } // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) { sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // is slice available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { sl = new ImageDataItem(*ch, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, (((size_t)s) * m_OffsetTable[2] + ((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // slice is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir mussen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, s); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, 1); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); if (IsSliceSet_unlocked(s, t, n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetSliceData_unlocked(s, t, n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateSliceData_unlocked(s, t, n, data, importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData(int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetVolumeData_unlocked(t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetVolumeData_unlocked( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidVolume(t, n) == false) return nullptr; ImageDataItemPointer ch, vol; // volume directly available? int pos = GetVolumeIndex(t, n); vol = m_Volumes[pos]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) return vol; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, (((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); vol->SetComplete(true); return m_Volumes[pos] = vol; } // let's see if all slices of the volume are set, so that we can (could) combine them to a volume bool complete = true; unsigned int s; for (s = 0; s < m_Dimensions[2]; ++s) { if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() == nullptr) { complete = false; break; } } if (complete) { // if there is only single slice we do not need to combine anything if (m_Dimensions[2] <= 1) { ImageDataItemPointer sl; sl = GetSliceData_unlocked(0, t, n, data, importMemoryManagement); vol = new ImageDataItem(*sl, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory); vol->SetComplete(true); } else { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); vol = m_Volumes[pos]; // ok, let's combine the slices! if (vol.GetPointer() == nullptr) { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, nullptr, true); } vol->SetComplete(true); size_t size = m_OffsetTable[2] * (ptypeSize); for (s = 0; s < m_Dimensions[2]; ++s) { int posSl; ImageDataItemPointer sl; posSl = GetSliceIndex(s, t, n); sl = m_Slices[posSl]; if (sl->GetParent() != vol) { // copy data of slices in volume size_t offset = ((size_t)s) * size; std::memcpy(static_cast(vol->GetData()) + offset, sl->GetData(), size); // replace old slice with reference to volume sl = new ImageDataItem( *vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * size); sl->SetComplete(true); m_Slices[posSl] = sl; } } } return m_Volumes[pos] = vol; } // volume is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, t); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, 1); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); if (IsVolumeSet_unlocked(t, n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetVolumeData_unlocked(t, n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateVolumeData_unlocked(t, n, data, importMemoryManagement); item->SetComplete(true); return item; } } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData(int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return GetChannelData_unlocked(n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::GetChannelData_unlocked( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { if (IsValidChannel(n) == false) return nullptr; ImageDataItemPointer ch, vol; ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return ch; // let's see if all volumes are set, so that we can (could) combine them to a channel if (IsChannelSet_unlocked(n)) { // if there is only one time frame we do not need to combine anything if (m_Dimensions[3] <= 1) { vol = GetVolumeData_unlocked(0, n, data, importMemoryManagement); ch = new ImageDataItem(*vol, m_ImageDescriptor, 0, m_ImageDescriptor->GetNumberOfDimensions(), data, importMemoryManagement == ManageMemory); ch->SetComplete(true); } else { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch = m_Channels[n]; // ok, let's combine the volumes! if (ch.GetPointer() == nullptr) ch = new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true); ch->SetComplete(true); size_t size = m_OffsetTable[m_Dimension - 1] * (ptypeSize); unsigned int t; auto slicesIt = m_Slices.begin() + n * m_Dimensions[2] * m_Dimensions[3]; for (t = 0; t < m_Dimensions[3]; ++t) { int posVol; ImageDataItemPointer vol; posVol = GetVolumeIndex(t, n); vol = GetVolumeData_unlocked(t, n, data, importMemoryManagement); if (vol->GetParent() != ch) { // copy data of volume in channel size_t offset = ((size_t)t) * m_OffsetTable[3] * (ptypeSize); std::memcpy(static_cast(ch->GetData()) + offset, vol->GetData(), size); // replace old volume with reference to channel vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, offset); vol->SetComplete(true); m_Volumes[posVol] = vol; // get rid of slices - they may point to old volume ImageDataItemPointer dnull = nullptr; for (unsigned int i = 0; i < m_Dimensions[2]; ++i, ++slicesIt) { assert(slicesIt != m_Slices.end()); *slicesIt = dnull; } } } } return m_Channels[n] = ch; } // channel is unavailable. Can we calculate it? if ((GetSource().IsNotNull()) && (GetSource()->Updating() == false)) { // ... wir muessen rechnen!!! .... m_RequestedRegion.SetIndex(0, 0); m_RequestedRegion.SetIndex(1, 0); m_RequestedRegion.SetIndex(2, 0); m_RequestedRegion.SetIndex(3, 0); m_RequestedRegion.SetIndex(4, n); m_RequestedRegion.SetSize(0, m_Dimensions[0]); m_RequestedRegion.SetSize(1, m_Dimensions[1]); m_RequestedRegion.SetSize(2, m_Dimensions[2]); m_RequestedRegion.SetSize(3, m_Dimensions[3]); m_RequestedRegion.SetSize(4, 1); m_RequestedRegionInitialized = true; GetSource()->Update(); // did it work? if (IsChannelSet_unlocked(n)) // yes: now we can call ourselves without the risk of a endless loop (see "if" above) return GetChannelData_unlocked(n, data, importMemoryManagement); else return nullptr; } else { ImageDataItemPointer item = AllocateChannelData_unlocked(n, data, importMemoryManagement); item->SetComplete(true); return item; } } bool mitk::Image::IsSliceSet(int s, int t, int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsSliceSet_unlocked(s, t, n); } bool mitk::Image::IsSliceSet_unlocked(int s, int t, int n) const { if (IsValidSlice(s, t, n) == false) return false; if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() != nullptr) { return true; } ImageDataItemPointer ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) { return true; } ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) { return true; } return false; } bool mitk::Image::IsVolumeSet(int t, int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsVolumeSet_unlocked(t, n); } bool mitk::Image::IsVolumeSet_unlocked(int t, int n) const { if (IsValidVolume(t, n) == false) return false; ImageDataItemPointer ch, vol; // volume directly available? vol = m_Volumes[GetVolumeIndex(t, n)]; if ((vol.GetPointer() != nullptr) && (vol->IsComplete())) return true; // is volume available as part of a channel that is available? ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return true; // let's see if all slices of the volume are set, so that we can (could) combine them to a volume unsigned int s; for (s = 0; s < m_Dimensions[2]; ++s) { if (m_Slices[GetSliceIndex(s, t, n)].GetPointer() == nullptr) { return false; } } return true; } bool mitk::Image::IsChannelSet(int n) const { MutexHolder lock(m_ImageDataArraysLock); return IsChannelSet_unlocked(n); } bool mitk::Image::IsChannelSet_unlocked(int n) const { if (IsValidChannel(n) == false) return false; ImageDataItemPointer ch, vol; ch = m_Channels[n]; if ((ch.GetPointer() != nullptr) && (ch->IsComplete())) return true; // let's see if all volumes are set, so that we can (could) combine them to a channel unsigned int t; for (t = 0; t < m_Dimensions[3]; ++t) { if (IsVolumeSet_unlocked(t, n) == false) { return false; } } return true; } bool mitk::Image::SetSlice(const void *data, int s, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportSlice(const_cast(data), s, t, n, CopyMemory); } bool mitk::Image::SetVolume(const void *data, int t, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportVolume(const_cast(data), t, n, CopyMemory); } bool mitk::Image::SetChannel(const void *data, int n) { // const_cast is no risk for ImportMemoryManagementType == CopyMemory return SetImportChannel(const_cast(data), n, CopyMemory); } bool mitk::Image::SetImportSlice(void *data, int s, int t, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidSlice(s, t, n) == false) return false; ImageDataItemPointer sl; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); if (IsSliceSet(s, t, n)) { sl = GetSliceData(s, t, n, data, importMemoryManagement); if (sl->GetManageMemory() == false) { sl = AllocateSliceData(s, t, n, data, importMemoryManagement); if (sl.GetPointer() == nullptr) return false; } if (sl->GetData() != data) std::memcpy(sl->GetData(), data, m_OffsetTable[2] * (ptypeSize)); sl->Modified(); // we have changed the data: call Modified()! Modified(); } else { sl = AllocateSliceData(s, t, n, data, importMemoryManagement); if (sl.GetPointer() == nullptr) return false; if (sl->GetData() != data) std::memcpy(sl->GetData(), data, m_OffsetTable[2] * (ptypeSize)); // we just added a missing slice, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(void *data, int t, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidVolume(t, n) == false) return false; const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer vol; if (IsVolumeSet(t, n)) { vol = GetVolumeData(t, n, data, importMemoryManagement); if (vol->GetManageMemory() == false) { vol = AllocateVolumeData(t, n, data, importMemoryManagement); if (vol.GetPointer() == nullptr) return false; } if (vol->GetData() != data) std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); vol->Modified(); vol->SetComplete(true); // we have changed the data: call Modified()! Modified(); } else { vol = AllocateVolumeData(t, n, data, importMemoryManagement); if (vol.GetPointer() == nullptr) return false; if (vol->GetData() != data) { std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); } vol->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData(vol->GetData()); // we just added a missing Volume, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } bool mitk::Image::SetImportVolume(const void *const_data, int t, int n) { return this->SetImportVolume(const_cast(const_data), t, n, CopyMemory); } bool mitk::Image::SetImportChannel(void *data, int n, ImportMemoryManagementType importMemoryManagement) { if (IsValidChannel(n) == false) return false; // channel descriptor const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ImageDataItemPointer ch; if (IsChannelSet(n)) { ch = GetChannelData(n, data, importMemoryManagement); if (ch->GetManageMemory() == false) { ch = AllocateChannelData(n, data, importMemoryManagement); if (ch.GetPointer() == nullptr) return false; } if (ch->GetData() != data) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); ch->Modified(); ch->SetComplete(true); // we have changed the data: call Modified()! Modified(); } else { ch = AllocateChannelData(n, data, importMemoryManagement); if (ch.GetPointer() == nullptr) return false; if (ch->GetData() != data) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); ch->SetComplete(true); this->m_ImageDescriptor->GetChannelDescriptor(n).SetData(ch->GetData()); // we just added a missing Channel, which is not regarded as modification. // Therefore, we do not call Modified()! } return true; } void mitk::Image::Initialize() { ImageDataItemPointerArray::iterator it, end; for (it = m_Slices.begin(), end = m_Slices.end(); it != end; ++it) { (*it) = nullptr; } for (it = m_Volumes.begin(), end = m_Volumes.end(); it != end; ++it) { (*it) = nullptr; } for (it = m_Channels.begin(), end = m_Channels.end(); it != end; ++it) { (*it) = nullptr; } m_CompleteData = nullptr; if (m_ImageStatistics == nullptr) { m_ImageStatistics = new mitk::ImageStatisticsHolder(this); } SetRequestedRegionToLargestPossibleRegion(); } void mitk::Image::Initialize(const mitk::ImageDescriptor::Pointer inDesc) { // store the descriptor this->m_ImageDescriptor = inDesc; // initialize image this->Initialize( inDesc->GetChannelDescriptor(0).GetPixelType(), inDesc->GetNumberOfDimensions(), inDesc->GetDimensions(), 1); } void mitk::Image::Initialize(const mitk::PixelType &type, unsigned int dimension, const unsigned int *dimensions, unsigned int channels) { Clear(); m_Dimension = dimension; if (!dimensions) itkExceptionMacro(<< "invalid zero dimension image"); unsigned int i; for (i = 0; i < dimension; ++i) { if (dimensions[i] < 1) itkExceptionMacro(<< "invalid dimension[" << i << "]: " << dimensions[i]); } // create new array since the old was deleted m_Dimensions = new unsigned int[MAX_IMAGE_DIMENSIONS]; // initialize the first four dimensions to 1, the remaining 4 to 0 FILL_C_ARRAY(m_Dimensions, 4, 1u); FILL_C_ARRAY((m_Dimensions + 4), 4, 0u); // copy in the passed dimension information std::memcpy(m_Dimensions, dimensions, sizeof(unsigned int) * m_Dimension); this->m_ImageDescriptor = mitk::ImageDescriptor::New(); this->m_ImageDescriptor->Initialize(this->m_Dimensions, this->m_Dimension); for (i = 0; i < 4; ++i) { m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, m_Dimensions[i]); } m_LargestPossibleRegion.SetIndex(i, 0); m_LargestPossibleRegion.SetSize(i, channels); if (m_LargestPossibleRegion.GetNumberOfPixels() == 0) { delete[] m_Dimensions; m_Dimensions = nullptr; return; } for (unsigned int i = 0u; i < channels; i++) { this->m_ImageDescriptor->AddNewChannel(type); } PlaneGeometry::Pointer planegeometry = PlaneGeometry::New(); planegeometry->InitializeStandardPlane(m_Dimensions[0], m_Dimensions[1]); SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(planegeometry, m_Dimensions[2]); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); for (TimeStepType step = 0; step < timeGeometry->CountTimeSteps(); ++step) { timeGeometry->GetGeometryForTimeStep(step)->ImageGeometryOn(); } SetTimeGeometry(timeGeometry); ImageDataItemPointer dnull = nullptr; m_Channels.assign(GetNumberOfChannels(), dnull); m_Volumes.assign(GetNumberOfChannels() * m_Dimensions[3], dnull); m_Slices.assign(GetNumberOfChannels() * m_Dimensions[3] * m_Dimensions[2], dnull); ComputeOffsetTable(); Initialize(); m_Initialized = true; } void mitk::Image::Initialize(const mitk::PixelType &type, const mitk::BaseGeometry &geometry, unsigned int channels, int tDim) { mitk::ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry.Clone(), tDim); this->Initialize(type, *timeGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::PixelType &type, const mitk::TimeGeometry &geometry, unsigned int channels, int tDim) { unsigned int dimensions[5]; dimensions[0] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(0) + 0.5); dimensions[1] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(1) + 0.5); dimensions[2] = (unsigned int)(geometry.GetGeometryForTimeStep(0)->GetExtent(2) + 0.5); dimensions[3] = (tDim > 0) ? tDim : geometry.CountTimeSteps(); dimensions[4] = 0; unsigned int dimension = 2; if (dimensions[2] > 1) dimension = 3; if (dimensions[3] > 1) dimension = 4; Initialize(type, dimension, dimensions, channels); if (geometry.CountTimeSteps() > 1) { TimeGeometry::Pointer cloned = geometry.Clone(); SetTimeGeometry(cloned.GetPointer()); // make sure the image geometry flag is properly set for all time steps for (TimeStepType step = 0; step < cloned->CountTimeSteps(); ++step) { if (!cloned->GetGeometryCloneForTimeStep(step)->GetImageGeometry()) { MITK_WARN("Image.3DnT.Initialize") << " Attempt to initialize an image with a non-image geometry. " "Re-interpretting the initialization geometry for timestep " << step << " as image geometry, the original geometry remains unchanged."; cloned->GetGeometryForTimeStep(step)->ImageGeometryOn(); } } } else { // make sure the image geometry coming from outside has proper value of the image geometry flag BaseGeometry::Pointer cloned = geometry.GetGeometryCloneForTimeStep(0)->Clone(); if (!cloned->GetImageGeometry()) { MITK_WARN("Image.Initialize") << " Attempt to initialize an image with a non-image geometry. Re-interpretting " "the initialization geometry as image geometry, the original geometry remains " "unchanged."; cloned->ImageGeometryOn(); } Superclass::SetGeometry(cloned); } } void mitk::Image::Initialize(const mitk::PixelType &type, int sDim, const mitk::PlaneGeometry &geometry2d, unsigned int channels, int tDim) { SlicedGeometry3D::Pointer slicedGeometry = SlicedGeometry3D::New(); slicedGeometry->InitializeEvenlySpaced(geometry2d.Clone(), sDim); Initialize(type, *slicedGeometry, channels, tDim); } void mitk::Image::Initialize(const mitk::Image *image) { Initialize(image->GetPixelType(), *image->GetTimeGeometry()); } void mitk::Image::Initialize(vtkImageData *vtkimagedata, int channels, int tDim, int sDim, int pDim) { if (vtkimagedata == nullptr) return; m_Dimension = vtkimagedata->GetDataDimension(); unsigned int i, *tmpDimensions = new unsigned int[m_Dimension > 4 ? m_Dimension : 4]; for (i = 0; i < m_Dimension; ++i) tmpDimensions[i] = vtkimagedata->GetDimensions()[i]; if (m_Dimension < 4) { unsigned int *p; for (i = 0, p = tmpDimensions + m_Dimension; i < 4 - m_Dimension; ++i, ++p) *p = 1; } if (pDim >= 0) { tmpDimensions[1] = pDim; if (m_Dimension < 2) m_Dimension = 2; } if (sDim >= 0) { tmpDimensions[2] = sDim; if (m_Dimension < 3) m_Dimension = 3; } if (tDim >= 0) { tmpDimensions[3] = tDim; if (m_Dimension < 4) m_Dimension = 4; } mitk::PixelType pixelType(MakePixelType(vtkimagedata)); Initialize(pixelType, m_Dimension, tmpDimensions, channels); const double *spacinglist = vtkimagedata->GetSpacing(); Vector3D spacing; FillVector3D(spacing, spacinglist[0], 1.0, 1.0); if (m_Dimension >= 2) spacing[1] = spacinglist[1]; if (m_Dimension >= 3) spacing[2] = spacinglist[2]; // access origin of vtkImage Point3D origin; double vtkorigin[3]; vtkimagedata->GetOrigin(vtkorigin); FillVector3D(origin, vtkorigin[0], 0.0, 0.0); if (m_Dimension >= 2) origin[1] = vtkorigin[1]; if (m_Dimension >= 3) origin[2] = vtkorigin[2]; SlicedGeometry3D *slicedGeometry = GetSlicedGeometry(0); // re-initialize PlaneGeometry with origin and direction auto *planeGeometry = static_cast(slicedGeometry->GetPlaneGeometry(0)); planeGeometry->SetOrigin(origin); // re-initialize SlicedGeometry3D slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, m_Dimensions[3]); SetTimeGeometry(timeGeometry); delete[] tmpDimensions; } bool mitk::Image::IsValidSlice(int s, int t, int n) const { if (m_Initialized) return ((s >= 0) && (s < (int)m_Dimensions[2]) && (t >= 0) && (t < (int)m_Dimensions[3]) && (n >= 0) && (n < (int)GetNumberOfChannels())); else return false; } bool mitk::Image::IsValidVolume(int t, int n) const { if (m_Initialized) return IsValidSlice(0, t, n); else return false; } bool mitk::Image::IsValidChannel(int n) const { if (m_Initialized) return IsValidSlice(0, 0, n); else return false; } void mitk::Image::ComputeOffsetTable() { if (m_OffsetTable != nullptr) delete[] m_OffsetTable; m_OffsetTable = new size_t[m_Dimension > 4 ? m_Dimension + 1 : 4 + 1]; unsigned int i; size_t num = 1; m_OffsetTable[0] = 1; for (i = 0; i < m_Dimension; ++i) { num *= m_Dimensions[i]; m_OffsetTable[i + 1] = num; } for (; i < 4; ++i) m_OffsetTable[i + 1] = num; } bool mitk::Image::IsValidTimeStep(int t) const { return ((m_Dimension >= 4 && t <= (int)m_Dimensions[3] && t > 0) || (t == 0)); } void mitk::Image::Expand(unsigned int timeSteps) { if (timeSteps < 1) itkExceptionMacro(<< "Invalid timestep in Image!"); Superclass::Expand(timeSteps); } int mitk::Image::GetSliceIndex(int s, int t, int n) const { if (IsValidSlice(s, t, n) == false) return false; return ((size_t)s) + ((size_t)t) * m_Dimensions[2] + ((size_t)n) * m_Dimensions[3] * m_Dimensions[2]; //?? } int mitk::Image::GetVolumeIndex(int t, int n) const { if (IsValidVolume(t, n) == false) return false; return ((size_t)t) + ((size_t)n) * m_Dimensions[3]; //?? } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateSliceData_unlocked(s, t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateSliceData_unlocked( int s, int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { int pos; pos = GetSliceIndex(s, t, n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is slice available as part of a volume that is available? ImageDataItemPointer sl, ch, vol; vol = m_Volumes[GetVolumeIndex(t, n)]; if (vol.GetPointer() != nullptr) { sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // is slice available as part of a channel that is available? ch = m_Channels[n]; if (ch.GetPointer() != nullptr) { sl = new ImageDataItem(*ch, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, (((size_t)s) * m_OffsetTable[2] + ((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; } // allocate new volume (instead of a single slice to keep data together!) m_Volumes[GetVolumeIndex(t, n)] = vol = AllocateVolumeData_unlocked(t, n, nullptr, importMemoryManagement); sl = new ImageDataItem(*vol, m_ImageDescriptor, t, 2, data, importMemoryManagement == ManageMemory, ((size_t)s) * m_OffsetTable[2] * (ptypeSize)); sl->SetComplete(true); return m_Slices[pos] = sl; ////ALTERNATIVE: //// allocate new slice // sl=new ImageDataItem(*m_PixelType, 2, m_Dimensions); // m_Slices[pos]=sl; // return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateVolumeData_unlocked(t, n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateVolumeData_unlocked( int t, int n, void *data, ImportMemoryManagementType importMemoryManagement) const { int pos; pos = GetVolumeIndex(t, n); const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); // is volume available as part of a channel that is available? ImageDataItemPointer ch, vol; ch = m_Channels[n]; if (ch.GetPointer() != nullptr) { vol = new ImageDataItem(*ch, m_ImageDescriptor, t, 3, data, importMemoryManagement == ManageMemory, (((size_t)t) * m_OffsetTable[3]) * (ptypeSize)); return m_Volumes[pos] = vol; } mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(n); // allocate new volume if (importMemoryManagement == CopyMemory) { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, nullptr, true); if (data != nullptr) std::memcpy(vol->GetData(), data, m_OffsetTable[3] * (ptypeSize)); } else { vol = new ImageDataItem(chPixelType, t, 3, m_Dimensions, data, importMemoryManagement == ManageMemory); } m_Volumes[pos] = vol; return vol; } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { MutexHolder lock(m_ImageDataArraysLock); return AllocateChannelData_unlocked(n, data, importMemoryManagement); } mitk::Image::ImageDataItemPointer mitk::Image::AllocateChannelData_unlocked( int n, void *data, ImportMemoryManagementType importMemoryManagement) const { ImageDataItemPointer ch; // allocate new channel if (importMemoryManagement == CopyMemory) { const size_t ptypeSize = this->m_ImageDescriptor->GetChannelTypeById(n).GetSize(); ch = new ImageDataItem(this->m_ImageDescriptor, -1, nullptr, true); if (data != nullptr) std::memcpy(ch->GetData(), data, m_OffsetTable[4] * (ptypeSize)); } else { ch = new ImageDataItem(this->m_ImageDescriptor, -1, data, importMemoryManagement == ManageMemory); } m_Channels[n] = ch; return ch; } unsigned int *mitk::Image::GetDimensions() const { return m_Dimensions; } void mitk::Image::Clear() { Superclass::Clear(); delete[] m_Dimensions; m_Dimensions = nullptr; } void mitk::Image::SetGeometry(BaseGeometry *aGeometry3D) { // Please be aware of the 0.5 offset/pixel-center issue! See Geometry documentation for further information if (aGeometry3D->GetImageGeometry() == false) { MITK_INFO << "WARNING: Applied a non-image geometry onto an image. Please be SURE that this geometry is " "pixel-center-based! If it is not, you need to call " "Geometry3D->ChangeImageGeometryConsideringOriginOffset(true) before calling image->setGeometry(..)\n"; } Superclass::SetGeometry(aGeometry3D); for (TimeStepType step = 0; step < GetTimeGeometry()->CountTimeSteps(); ++step) GetTimeGeometry()->GetGeometryForTimeStep(step)->ImageGeometryOn(); } void mitk::Image::PrintSelf(std::ostream &os, itk::Indent indent) const { if (m_Initialized) { unsigned char i; os << indent << " Dimension: " << m_Dimension << std::endl; os << indent << " Dimensions: "; for (i = 0; i < m_Dimension; ++i) os << GetDimension(i) << " "; os << std::endl; for (unsigned int ch = 0; ch < this->m_ImageDescriptor->GetNumberOfChannels(); ch++) { mitk::PixelType chPixelType = this->m_ImageDescriptor->GetChannelTypeById(ch); os << indent << " Channel: " << this->m_ImageDescriptor->GetChannelName(ch) << std::endl; os << indent << " PixelType: " << chPixelType.GetPixelTypeAsString() << std::endl; os << indent << " BytesPerElement: " << chPixelType.GetSize() << std::endl; os << indent << " ComponentType: " << chPixelType.GetComponentTypeAsString() << std::endl; os << indent << " NumberOfComponents: " << chPixelType.GetNumberOfComponents() << std::endl; os << indent << " BitsPerComponent: " << chPixelType.GetBitsPerComponent() << std::endl; } } else { os << indent << " Image not initialized: m_Initialized: false" << std::endl; } Superclass::PrintSelf(os, indent); } bool mitk::Image::IsRotated() const { const mitk::BaseGeometry *geo = this->GetGeometry(); bool ret = false; if (geo) { const vnl_matrix_fixed &mx = geo->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::ScalarType ref = 0; for (short k = 0; k < 3; ++k) ref += mx[k][k]; ref /= 1000; // Arbitrary value; if a non-diagonal (nd) element is bigger then this, matrix is considered nd. for (short i = 0; i < 3; ++i) { for (short j = 0; j < 3; ++j) { if (i != j) { if (std::abs(mx[i][j]) > ref) // matrix is nd ret = true; } } } } return ret; } bool mitk::Equal(const mitk::Image &leftHandSide, const mitk::Image &rightHandSide, ScalarType eps, bool verbose) { bool returnValue = true; // Dimensionality if (rightHandSide.GetDimension() != leftHandSide.GetDimension()) { if (verbose) { MITK_INFO << "[( Image )] Dimensionality differs."; MITK_INFO << "leftHandSide is " << leftHandSide.GetDimension() << "rightHandSide is " << rightHandSide.GetDimension(); } returnValue = false; } // Pair-wise dimension (size) comparison unsigned int minDimensionality = std::min(rightHandSide.GetDimension(), leftHandSide.GetDimension()); for (unsigned int i = 0; i < minDimensionality; ++i) { if (rightHandSide.GetDimension(i) != leftHandSide.GetDimension(i)) { returnValue = false; if (verbose) { MITK_INFO << "[( Image )] dimension differs."; MITK_INFO << "leftHandSide->GetDimension(" << i << ") is " << leftHandSide.GetDimension(i) << "rightHandSide->GetDimension(" << i << ") is " << rightHandSide.GetDimension(i); } } } // Pixeltype mitk::PixelType pixelTypeRightHandSide = rightHandSide.GetPixelType(); mitk::PixelType pixelTypeLeftHandSide = leftHandSide.GetPixelType(); if (!(pixelTypeRightHandSide == pixelTypeLeftHandSide)) { if (verbose) { MITK_INFO << "[( Image )] PixelType differs."; MITK_INFO << "leftHandSide is " << pixelTypeLeftHandSide.GetTypeAsString() << "rightHandSide is " << pixelTypeRightHandSide.GetTypeAsString(); } returnValue = false; } // Geometries if (!mitk::Equal(*leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose)) { if (verbose) { MITK_INFO << "[( Image )] Geometries differ."; } returnValue = false; } // Pixel values - default mode [ 0 threshold in difference ] // compare only if all previous checks were successfull, otherwise the ITK filter will throw an exception if (returnValue) { mitk::CompareImageDataFilter::Pointer compareFilter = mitk::CompareImageDataFilter::New(); compareFilter->SetInput(0, &rightHandSide); compareFilter->SetInput(1, &leftHandSide); compareFilter->SetTolerance(eps); compareFilter->Update(); if ((!compareFilter->GetResult())) { returnValue = false; if (verbose) { MITK_INFO << "[(Image)] Pixel values differ: "; compareFilter->GetCompareResults().PrintSelf(); } } } return returnValue; } diff --git a/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp b/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp index 77e58bf44c..239f195d07 100644 --- a/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp +++ b/Modules/Core/src/DataManagement/mitkImageAccessorBase.cpp @@ -1,184 +1,184 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkImageAccessorBase.h" #include "mitkImage.h" mitk::ImageAccessorBase::ThreadIDType mitk::ImageAccessorBase::CurrentThreadHandle() { #ifdef ITK_USE_SPROC return GetCurrentThreadId(); #endif #ifdef ITK_USE_PTHREADS return pthread_self(); #endif #ifdef ITK_USE_WIN32_THREADS return GetCurrentThreadId(); #endif } bool mitk::ImageAccessorBase::CompareThreadHandles(mitk::ImageAccessorBase::ThreadIDType handle1, mitk::ImageAccessorBase::ThreadIDType handle2) { return handle1 == handle2; } mitk::ImageAccessorBase::~ImageAccessorBase() { } mitk::ImageAccessorBase::ImageAccessorBase(ImageConstPointer image, const ImageDataItem *imageDataItem, int OptionFlags) : // m_Image(iP) //, imageDataItem(iDI) m_SubRegion(nullptr), m_Options(OptionFlags), m_CoherentMemory(false) { m_Thread = CurrentThreadHandle(); // Initialize WaitLock m_WaitLock = new ImageAccessorWaitLock(); m_WaitLock->m_WaiterCount = 0; // Check validity of ImageAccessor // Is there an Image? /* if(!m_Image) { mitkThrow() << "Invalid ImageAccessor: No Image was specified in constructor of ImageAccessor"; } */ if (image) { // Make sure, that the Image is initialized properly if (image->m_Initialized == false) { if (image->GetSource().IsNull()) { mitkThrow() << "ImageAccessor: No image source is defined"; } - image->m_ReadWriteLock.Lock(); + image->m_ReadWriteLock.lock(); if (image->GetSource()->Updating() == false) { image->GetSource()->UpdateOutputInformation(); } - image->m_ReadWriteLock.Unlock(); + image->m_ReadWriteLock.unlock(); } } // Investigate 4 cases of possible image parts/regions // Case 1: No ImageDataItem and no Subregion => Whole Image is accessed if (imageDataItem == nullptr && m_SubRegion == nullptr) { m_CoherentMemory = true; // Organize first image channel - image->m_ReadWriteLock.Lock(); + image->m_ReadWriteLock.lock(); imageDataItem = image->GetChannelData(); - image->m_ReadWriteLock.Unlock(); + image->m_ReadWriteLock.unlock(); // Set memory area m_AddressBegin = imageDataItem->m_Data; m_AddressEnd = (unsigned char *)m_AddressBegin + imageDataItem->m_Size; } // Case 2: ImageDataItem but no Subregion if (imageDataItem && m_SubRegion == nullptr) { m_CoherentMemory = true; // Set memory area m_AddressBegin = imageDataItem->m_Data; m_AddressEnd = (unsigned char *)m_AddressBegin + imageDataItem->m_Size; } // Case 3: No ImageDataItem but a SubRegion if (imageDataItem == nullptr && m_SubRegion) { mitkThrow() << "Invalid ImageAccessor: The use of a SubRegion is not supported (yet)."; } // Case 4: ImageDataItem and SubRegion if (imageDataItem == nullptr && m_SubRegion) { mitkThrow() << "Invalid ImageAccessor: The use of a SubRegion is not supported (yet)."; } } /** \brief Computes if there is an Overlap of the image part between this instantiation and another ImageAccessor object * \throws mitk::Exception if memory area is incoherent (not supported yet) */ bool mitk::ImageAccessorBase::Overlap(const ImageAccessorBase *iAB) { if (m_CoherentMemory) { if ((iAB->m_AddressBegin >= m_AddressBegin && iAB->m_AddressBegin < m_AddressEnd) || (iAB->m_AddressEnd > m_AddressBegin && iAB->m_AddressEnd <= m_AddressEnd)) { return true; } if ((m_AddressBegin >= iAB->m_AddressBegin && m_AddressBegin < iAB->m_AddressEnd) || (m_AddressEnd > iAB->m_AddressBegin && m_AddressEnd <= iAB->m_AddressEnd)) { return true; } } else { - GetImage()->m_ReadWriteLock.Unlock(); + GetImage()->m_ReadWriteLock.unlock(); mitkThrow() << "ImageAccessor: incoherent memory area is not supported yet"; } return false; } /** \brief Uses the WaitLock to wait for another ImageAccessor*/ void mitk::ImageAccessorBase::WaitForReleaseOf(ImageAccessorWaitLock *wL) { - wL->m_Mutex.Lock(); + wL->m_Mutex.lock(); // Decrement wL->m_WaiterCount -= 1; // If there are no more waiting ImageAccessors, delete the Mutex // (Der Letzte macht das Licht aus!) if (wL->m_WaiterCount <= 0) { - wL->m_Mutex.Unlock(); + wL->m_Mutex.unlock(); delete wL; } else { - wL->m_Mutex.Unlock(); + wL->m_Mutex.unlock(); } } void mitk::ImageAccessorBase::PreventRecursiveMutexLock(mitk::ImageAccessorBase *iAB) { #ifdef MITK_USE_RECURSIVE_MUTEX_PREVENTION // Prevent deadlock ThreadIDType id = CurrentThreadHandle(); if (CompareThreadHandles(id, iAB->m_Thread)) { - GetImage()->m_ReadWriteLock.Unlock(); + GetImage()->m_ReadWriteLock.unlock(); mitkThrow() << "Prohibited image access: the requested image part is already in use and cannot be requested recursively!"; } #endif } diff --git a/Modules/Core/src/DataManagement/mitkImageDataItem.cpp b/Modules/Core/src/DataManagement/mitkImageDataItem.cpp index cc252716cb..407ea0a0ee 100644 --- a/Modules/Core/src/DataManagement/mitkImageDataItem.cpp +++ b/Modules/Core/src/DataManagement/mitkImageDataItem.cpp @@ -1,334 +1,334 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkImageDataItem.h" #include "mitkMemoryUtilities.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::ImageDataItem::ImageDataItem(const ImageDataItem &aParent, const mitk::ImageDescriptor::Pointer desc, int timestep, unsigned int dimension, void *data, bool manageMemory, size_t offset) : m_Data(static_cast(aParent.m_Data) + offset), m_PixelType(new mitk::PixelType(aParent.GetPixelType())), m_ManageMemory(false), m_VtkImageData(nullptr), m_VtkImageReadAccessor(nullptr), m_VtkImageWriteAccessor(nullptr), m_Offset(offset), m_IsComplete(false), m_Size(0), m_Parent(&aParent), m_Dimension(dimension), m_Timestep(timestep) { // compute size // const unsigned int *dims = desc->GetDimensions(); for (unsigned int i = 0; i < dimension; i++) { m_Dimensions[i] = desc->GetDimensions()[i]; } this->ComputeItemSize(m_Dimensions, dimension); if (data != nullptr && data != m_Data) { memcpy(m_Data, data, m_Size); if (manageMemory) { delete[](unsigned char *) data; } } m_ReferenceCount = 0; } mitk::ImageDataItem::~ImageDataItem() { if (m_VtkImageData != nullptr) { m_VtkImageData->Delete(); } if (m_VtkImageReadAccessor != nullptr) { delete m_VtkImageReadAccessor; } if (m_VtkImageWriteAccessor != nullptr) { delete m_VtkImageWriteAccessor; } if (m_Parent.IsNull()) { if (m_ManageMemory) delete[] m_Data; } delete m_PixelType; } mitk::ImageDataItem::ImageDataItem(const mitk::ImageDescriptor::Pointer desc, int timestep, void *data, bool manageMemory) : m_Data(static_cast(data)), m_PixelType(new mitk::PixelType(desc->GetChannelDescriptor(0).GetPixelType())), m_ManageMemory(manageMemory), m_VtkImageData(nullptr), m_VtkImageReadAccessor(nullptr), m_VtkImageWriteAccessor(nullptr), m_Offset(0), m_IsComplete(false), m_Size(0), m_Dimension(desc->GetNumberOfDimensions()), m_Timestep(timestep) { // compute size const unsigned int *dimensions = desc->GetDimensions(); for (unsigned int i = 0; i < m_Dimension; i++) { m_Dimensions[i] = dimensions[i]; } this->ComputeItemSize(m_Dimensions, m_Dimension); if (m_Data == nullptr) { m_Data = mitk::MemoryUtilities::AllocateElements(m_Size); m_ManageMemory = true; } m_ReferenceCount = 0; } mitk::ImageDataItem::ImageDataItem(const mitk::PixelType &type, int timestep, unsigned int dimension, unsigned int *dimensions, void *data, bool manageMemory) : m_Data(static_cast(data)), m_PixelType(new mitk::PixelType(type)), m_ManageMemory(manageMemory), m_VtkImageData(nullptr), m_VtkImageReadAccessor(nullptr), m_VtkImageWriteAccessor(nullptr), m_Offset(0), m_IsComplete(false), m_Size(0), m_Parent(nullptr), m_Dimension(dimension), m_Timestep(timestep) { for (unsigned int i = 0; i < m_Dimension; i++) { m_Dimensions[i] = dimensions[i]; } this->ComputeItemSize(dimensions, dimension); if (m_Data == nullptr) { m_Data = mitk::MemoryUtilities::AllocateElements(m_Size); m_ManageMemory = true; } m_ReferenceCount = 0; } mitk::ImageDataItem::ImageDataItem(const ImageDataItem &other) : itk::LightObject(), m_Data(other.m_Data), m_PixelType(new mitk::PixelType(*other.m_PixelType)), m_ManageMemory(other.m_ManageMemory), m_VtkImageData(nullptr), m_VtkImageReadAccessor(nullptr), m_VtkImageWriteAccessor(nullptr), m_Offset(other.m_Offset), m_IsComplete(other.m_IsComplete), m_Size(other.m_Size), m_Parent(other.m_Parent), m_Dimension(other.m_Dimension), m_Timestep(other.m_Timestep) { // copy m_Data ?? for (int i = 0; i < MAX_IMAGE_DIMENSIONS; ++i) m_Dimensions[i] = other.m_Dimensions[i]; } itk::LightObject::Pointer mitk::ImageDataItem::InternalClone() const { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void mitk::ImageDataItem::ComputeItemSize(const unsigned int *dimensions, unsigned int dimension) { m_Size = m_PixelType->GetSize(); for (unsigned int i = 0; i < dimension; i++) { m_Size *= *(dimensions + i); } } void mitk::ImageDataItem::ConstructVtkImageData(ImageConstPointer iP) const { vtkImageData *inData = vtkImageData::New(); vtkDataArray *scalars = nullptr; const unsigned int *dims = m_Dimensions; const unsigned int dim = m_Dimension; unsigned long size = 0; if (dim == 1) { inData->SetDimensions(dims[0] - 1, 1, 1); size = dims[0]; inData->SetOrigin(((mitk::ScalarType)dims[0]) / 2.0, 0, 0); } else if (dim == 2) { inData->SetDimensions(dims[0], dims[1], 1); size = dims[0] * dims[1]; inData->SetOrigin(((mitk::ScalarType)dims[0]) / 2.0f, ((mitk::ScalarType)dims[1]) / 2.0f, 0); } else if (dim >= 3) { inData->SetDimensions(dims[0], dims[1], dims[2]); size = dims[0] * dims[1] * dims[2]; // Test // inData->SetOrigin( (float) dims[0] / 2.0f, (float) dims[1] / 2.0f, (float) dims[2] / 2.0f ); inData->SetOrigin(0, 0, 0); } else { inData->Delete(); return; } if (m_Timestep >= 0) { SlicedGeometry3D *geom3d; geom3d = iP->GetSlicedGeometry(m_Timestep); const mitk::Vector3D vspacing = geom3d->GetSpacing(); double dspacing[3] = {vspacing[0], vspacing[1], vspacing[2]}; inData->SetSpacing(dspacing); } - if (m_PixelType->GetComponentType() == itk::ImageIOBase::CHAR) + if (m_PixelType->GetComponentType() == itk::IOComponentEnum::CHAR) { scalars = vtkCharArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::UCHAR) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::UCHAR) { scalars = vtkUnsignedCharArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::SHORT) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::SHORT) { scalars = vtkShortArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::USHORT) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::USHORT) { scalars = vtkUnsignedShortArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::INT) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::INT) { scalars = vtkIntArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::UINT) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::UINT) { scalars = vtkUnsignedIntArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::LONG) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::LONG) { scalars = vtkLongArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::ULONG) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::ULONG) { scalars = vtkUnsignedLongArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::FLOAT) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::FLOAT) { scalars = vtkFloatArray::New(); } - else if (m_PixelType->GetComponentType() == itk::ImageIOBase::DOUBLE) + else if (m_PixelType->GetComponentType() == itk::IOComponentEnum::DOUBLE) { scalars = vtkDoubleArray::New(); } else { inData->Delete(); return; } m_VtkImageData = inData; // set mitk imageDataItem void array to vtk scalar values scalars->SetNumberOfComponents(m_PixelType->GetNumberOfComponents()); scalars->SetVoidArray(m_Data, size * m_PixelType->GetNumberOfComponents(), 1); m_VtkImageData->GetPointData()->SetScalars(scalars); scalars->Delete(); } void mitk::ImageDataItem::Modified() const { if (m_VtkImageData) m_VtkImageData->Modified(); } mitk::ImageVtkReadAccessor *mitk::ImageDataItem::GetVtkImageAccessor(mitk::ImageDataItem::ImageConstPointer iP) const { if (m_VtkImageData == nullptr) { ConstructVtkImageData(iP); } if (m_VtkImageReadAccessor == nullptr) { m_VtkImageReadAccessor = new ImageVtkReadAccessor(iP, this, m_VtkImageData); } return m_VtkImageReadAccessor; } mitk::ImageVtkWriteAccessor *mitk::ImageDataItem::GetVtkImageAccessor(ImagePointer iP) { if (m_VtkImageData == nullptr) { ConstructVtkImageData(iP.GetPointer()); } if (m_VtkImageWriteAccessor == nullptr) { m_VtkImageWriteAccessor = new ImageVtkWriteAccessor(iP, this, m_VtkImageData); } return m_VtkImageWriteAccessor; } diff --git a/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp index 328e0869ee..a1a62914a6 100644 --- a/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp +++ b/Modules/Core/src/DataManagement/mitkImageReadAccessor.cpp @@ -1,147 +1,147 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkImageReadAccessor.h" #include "mitkImage.h" mitk::ImageReadAccessor::ImageReadAccessor(ImageConstPointer image, const mitk::ImageDataItem *iDI, int OptionFlags) : ImageAccessorBase(image, iDI, OptionFlags), m_Image(image) { if (!(OptionFlags & ImageAccessorBase::IgnoreLock)) { try { OrganizeReadAccess(); } catch (...) { delete m_WaitLock; throw; } } } mitk::ImageReadAccessor::ImageReadAccessor(ImagePointer image, const mitk::ImageDataItem *iDI, int OptionFlags) : ImageAccessorBase(image.GetPointer(), iDI, OptionFlags), m_Image(image.GetPointer()) { if (!(OptionFlags & ImageAccessorBase::IgnoreLock)) { try { OrganizeReadAccess(); } catch (...) { delete m_WaitLock; throw; } } } mitk::ImageReadAccessor::ImageReadAccessor(const mitk::Image *image, const ImageDataItem *iDI) : ImageAccessorBase(image, iDI, ImageAccessorBase::DefaultBehavior), m_Image(image) { OrganizeReadAccess(); } mitk::ImageReadAccessor::~ImageReadAccessor() { if (!(m_Options & ImageAccessorBase::IgnoreLock)) { // Future work: In case of non-coherent memory, copied area needs to be deleted - m_Image->m_ReadWriteLock.Lock(); + m_Image->m_ReadWriteLock.lock(); // delete self from list of ImageReadAccessors in Image auto it = std::find(m_Image->m_Readers.begin(), m_Image->m_Readers.end(), this); m_Image->m_Readers.erase(it); // delete lock, if there are no waiting ImageAccessors if (m_WaitLock->m_WaiterCount <= 0) { - m_WaitLock->m_Mutex.Unlock(); + m_WaitLock->m_Mutex.unlock(); delete m_WaitLock; } else { - m_WaitLock->m_Mutex.Unlock(); + m_WaitLock->m_Mutex.unlock(); } - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); } else { delete m_WaitLock; } } const mitk::Image *mitk::ImageReadAccessor::GetImage() const { return m_Image.GetPointer(); } void mitk::ImageReadAccessor::OrganizeReadAccess() { - m_Image->m_ReadWriteLock.Lock(); + m_Image->m_ReadWriteLock.lock(); // Check, if there is any Write-Access going on if (m_Image->m_Writers.size() > 0) { // Check for every WriteAccessors, if the Region of this ImageAccessors overlaps // make sure this iterator is not used, when m_ReadWriteLock is Unlocked! auto it = m_Image->m_Writers.begin(); for (; it != m_Image->m_Writers.end(); ++it) { ImageAccessorBase *w = *it; if (Overlap(w)) { // An Overlap was detected. There are two possibilities to deal with this situation: // Throw an exception or wait for the WriteAccessor w until it is released and start again with the request // afterwards. if (!(m_Options & ExceptionIfLocked)) { PreventRecursiveMutexLock(w); // WAIT w->Increment(); - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); ImageAccessorBase::WaitForReleaseOf(w->m_WaitLock); // after waiting for the WriteAccessor w, start this method again OrganizeReadAccess(); return; } else { // THROW EXCEPTION - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); mitkThrowException(mitk::MemoryIsLockedException) << "The image part being ordered by the ImageAccessor is already in use and locked"; return; } } // if } // for } // if // Now, we know, that there is no conflict with a Write-Access // Lock the Mutex in ImageAccessorBase, to make sure that every other ImageAccessor has to wait if it locks the mutex - m_WaitLock->m_Mutex.Lock(); + m_WaitLock->m_Mutex.lock(); // insert self into readers list in Image m_Image->m_Readers.push_back(this); // printf("ReadAccess %d %d\n",(int) m_Image->m_Readers.size(),(int) m_Image->m_Writers.size()); // fflush(0); - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); } diff --git a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp index 181c995da1..811f2029c8 100644 --- a/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp +++ b/Modules/Core/src/DataManagement/mitkImageStatisticsHolder.cpp @@ -1,348 +1,348 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkImageStatisticsHolder.h" #include "mitkHistogramGenerator.h" #include #include "mitkImageAccessByItk.h" //#define BOUNDINGOBJECT_IGNORE mitk::ImageStatisticsHolder::ImageStatisticsHolder(mitk::Image *image) : m_Image(image) { m_CountOfMinValuedVoxels.resize(1, 0); m_CountOfMaxValuedVoxels.resize(1, 0); m_ScalarMin.resize(1, itk::NumericTraits::max()); m_ScalarMax.resize(1, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.resize(1, itk::NumericTraits::max()); m_Scalar2ndMax.resize(1, itk::NumericTraits::NonpositiveMin()); mitk::HistogramGenerator::Pointer generator = mitk::HistogramGenerator::New(); m_HistogramGeneratorObject = generator; } mitk::ImageStatisticsHolder::~ImageStatisticsHolder() { m_HistogramGeneratorObject = nullptr; } const mitk::ImageStatisticsHolder::HistogramType *mitk::ImageStatisticsHolder::GetScalarHistogram( int t, unsigned int /*component*/) { mitk::ImageTimeSelector *timeSelector = this->GetTimeSelector(); if (timeSelector != nullptr) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); auto *generator = static_cast(m_HistogramGeneratorObject.GetPointer()); generator->SetImage(timeSelector->GetOutput()); generator->ComputeHistogram(); return static_cast(generator->GetHistogram()); } return nullptr; } bool mitk::ImageStatisticsHolder::IsValidTimeStep(int t) const { return m_Image->IsValidTimeStep(t); } mitk::ImageTimeSelector::Pointer mitk::ImageStatisticsHolder::GetTimeSelector() { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(m_Image); return timeSelector; } void mitk::ImageStatisticsHolder::Expand(unsigned int timeSteps) { if (!m_Image->IsValidTimeStep(timeSteps - 1)) return; // The BaseData needs to be expanded, call the mitk::Image::Expand() method m_Image->Expand(timeSteps); if (timeSteps > m_ScalarMin.size()) { m_ScalarMin.resize(timeSteps, itk::NumericTraits::max()); m_ScalarMax.resize(timeSteps, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.resize(timeSteps, itk::NumericTraits::max()); m_Scalar2ndMax.resize(timeSteps, itk::NumericTraits::NonpositiveMin()); m_CountOfMinValuedVoxels.resize(timeSteps, 0); m_CountOfMaxValuedVoxels.resize(timeSteps, 0); } } void mitk::ImageStatisticsHolder::ResetImageStatistics() { m_ScalarMin.assign(1, itk::NumericTraits::max()); m_ScalarMax.assign(1, itk::NumericTraits::NonpositiveMin()); m_Scalar2ndMin.assign(1, itk::NumericTraits::max()); m_Scalar2ndMax.assign(1, itk::NumericTraits::NonpositiveMin()); m_CountOfMinValuedVoxels.assign(1, 0); m_CountOfMaxValuedVoxels.assign(1, 0); } /// \cond SKIP_DOXYGEN template void mitk::_ComputeExtremaInItkImage(const ItkImageType *itkImage, mitk::ImageStatisticsHolder *statisticsHolder, int t) { typename ItkImageType::RegionType region; region = itkImage->GetBufferedRegion(); if (region.Crop(itkImage->GetRequestedRegion()) == false) return; if (region != itkImage->GetRequestedRegion()) return; itk::ImageRegionConstIterator it(itkImage, region); typedef typename ItkImageType::PixelType TPixel; TPixel value = 0; if (statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep(t)) return; statisticsHolder->Expand(t + 1); // make sure we have initialized all arrays statisticsHolder->m_CountOfMinValuedVoxels[t] = 0; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0; statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t] = itk::NumericTraits::max(); statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t] = itk::NumericTraits::NonpositiveMin(); while (!it.IsAtEnd()) { value = it.Get(); #ifdef BOUNDINGOBJECT_IGNORE if (value > -32765) { #endif // update min if (value < statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t]; statisticsHolder->m_ScalarMin[t] = value; statisticsHolder->m_CountOfMinValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMin[t]) { ++statisticsHolder->m_CountOfMinValuedVoxels[t]; } else if (value < statisticsHolder->m_Scalar2ndMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = value; } // update max if (value > statisticsHolder->m_ScalarMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t]; statisticsHolder->m_ScalarMax[t] = value; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMax[t]) { ++statisticsHolder->m_CountOfMaxValuedVoxels[t]; } else if (value > statisticsHolder->m_Scalar2ndMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = value; } #ifdef BOUNDINGOBJECT_IGNORE } #endif ++it; } //// guard for wrong 2dMin/Max on single constant value images if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t]; } statisticsHolder->m_LastRecomputeTimeStamp.Modified(); } /// \endcond SKIP_DOXYGEN /// \cond SKIP_DOXYGEN template void mitk::_ComputeExtremaInItkVectorImage(const ItkImageType *itkImage, mitk::ImageStatisticsHolder *statisticsHolder, int t, unsigned int component) { typename ItkImageType::RegionType region; region = itkImage->GetBufferedRegion(); if (region.Crop(itkImage->GetRequestedRegion()) == false) return; if (region != itkImage->GetRequestedRegion()) return; itk::ImageRegionConstIterator it(itkImage, region); if (statisticsHolder == nullptr || !statisticsHolder->IsValidTimeStep(t)) return; statisticsHolder->Expand(t + 1); // make sure we have initialized all arrays statisticsHolder->m_CountOfMinValuedVoxels[t] = 0; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 0; statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t] = itk::NumericTraits::max(); statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t] = itk::NumericTraits::NonpositiveMin(); while (!it.IsAtEnd()) { double value = it.Get()[component]; #ifdef BOUNDINGOBJECT_IGNORE if (value > -32765) { #endif // update min if (value < statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMin[t]; statisticsHolder->m_ScalarMin[t] = value; statisticsHolder->m_CountOfMinValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMin[t]) { ++statisticsHolder->m_CountOfMinValuedVoxels[t]; } else if (value < statisticsHolder->m_Scalar2ndMin[t]) { statisticsHolder->m_Scalar2ndMin[t] = value; } // update max if (value > statisticsHolder->m_ScalarMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_ScalarMax[t]; statisticsHolder->m_ScalarMax[t] = value; statisticsHolder->m_CountOfMaxValuedVoxels[t] = 1; } else if (value == statisticsHolder->m_ScalarMax[t]) { ++statisticsHolder->m_CountOfMaxValuedVoxels[t]; } else if (value > statisticsHolder->m_Scalar2ndMax[t]) { statisticsHolder->m_Scalar2ndMax[t] = value; } #ifdef BOUNDINGOBJECT_IGNORE } #endif ++it; } //// guard for wrong 2dMin/Max on single constant value images if (statisticsHolder->m_ScalarMax[t] == statisticsHolder->m_ScalarMin[t]) { statisticsHolder->m_Scalar2ndMax[t] = statisticsHolder->m_Scalar2ndMin[t] = statisticsHolder->m_ScalarMax[t]; } statisticsHolder->m_LastRecomputeTimeStamp.Modified(); } /// \endcond SKIP_DOXYGEN void mitk::ImageStatisticsHolder::ComputeImageStatistics(int t, unsigned int component) { // timestep valid? if (!m_Image->IsValidTimeStep(t)) return; // image modified? if (this->m_Image->GetMTime() > m_LastRecomputeTimeStamp.GetMTime()) this->ResetImageStatistics(); Expand(t + 1); // do we have valid information already? if (m_ScalarMin[t] != itk::NumericTraits::max() || m_Scalar2ndMin[t] != itk::NumericTraits::max()) return; // Values already calculated before... // used to avoid statistics calculation on Odf images. property will be replaced as soons as bug 17928 is merged and // the diffusion image refactoring is complete. mitk::BoolProperty *isSh = dynamic_cast(m_Image->GetProperty("IsShImage").GetPointer()); mitk::BoolProperty *isOdf = dynamic_cast(m_Image->GetProperty("IsOdfImage").GetPointer()); const mitk::PixelType pType = m_Image->GetPixelType(0); - if (pType.GetNumberOfComponents() == 1 && (pType.GetPixelType() != itk::ImageIOBase::UNKNOWNPIXELTYPE) && - (pType.GetPixelType() != itk::ImageIOBase::VECTOR)) + if (pType.GetNumberOfComponents() == 1 && (pType.GetPixelType() != itk::IOPixelEnum::UNKNOWNPIXELTYPE) && + (pType.GetPixelType() != itk::IOPixelEnum::VECTOR)) { // recompute mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector(); if (timeSelector.IsNotNull()) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); const mitk::Image *image = timeSelector->GetOutput(); AccessByItk_2(image, _ComputeExtremaInItkImage, this, t); } } - else if (pType.GetPixelType() == itk::ImageIOBase::VECTOR && + else if (pType.GetPixelType() == itk::IOPixelEnum::VECTOR && (!isOdf || !isOdf->GetValue()) && (!isSh || !isSh->GetValue())) // we have a vector image { // recompute mitk::ImageTimeSelector::Pointer timeSelector = this->GetTimeSelector(); if (timeSelector.IsNotNull()) { timeSelector->SetTimeNr(t); timeSelector->UpdateLargestPossibleRegion(); const mitk::Image *image = timeSelector->GetOutput(); AccessVectorPixelTypeByItk_n(image, _ComputeExtremaInItkVectorImage, (this, t, component)); } } else { m_ScalarMin[t] = 0; m_ScalarMax[t] = 255; m_Scalar2ndMin[t] = 0; m_Scalar2ndMax[t] = 255; } } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMin(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_ScalarMin[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValueMax(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_ScalarMax[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMin(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_Scalar2ndMin[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetScalarValue2ndMax(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_Scalar2ndMax[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMinValuedVoxels(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_CountOfMinValuedVoxels[t]; } mitk::ScalarType mitk::ImageStatisticsHolder::GetCountOfMaxValuedVoxels(int t, unsigned int component) { ComputeImageStatistics(t, component); return m_CountOfMaxValuedVoxels[t]; } diff --git a/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp index acc2cdbd24..49335d337b 100644 --- a/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp +++ b/Modules/Core/src/DataManagement/mitkImageVtkReadAccessor.cpp @@ -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. ============================================================================*/ #include "mitkImageVtkReadAccessor.h" #include "mitkImage.h" #include const mitk::Image *mitk::ImageVtkReadAccessor::GetImage() const { return m_Image; } mitk::ImageVtkReadAccessor::ImageVtkReadAccessor(ImageConstPointer iP, const mitk::ImageDataItem *iDI, const vtkImageData *imageDataVtk) : ImageAccessorBase(iP, iDI), m_Image(iP.GetPointer()), m_ImageDataVtk(imageDataVtk) { - m_Image->m_VtkReadersLock.Lock(); + m_Image->m_VtkReadersLock.lock(); m_Image->m_VtkReaders.push_back(this); // printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size()); - m_Image->m_VtkReadersLock.Unlock(); + m_Image->m_VtkReadersLock.unlock(); } mitk::ImageVtkReadAccessor::~ImageVtkReadAccessor() { - m_Image->m_VtkReadersLock.Lock(); + m_Image->m_VtkReadersLock.lock(); auto it = std::find(m_Image->m_VtkReaders.begin(), m_Image->m_VtkReaders.end(), this); if (it != m_Image->m_VtkReaders.end()) { m_Image->m_VtkReaders.erase(it); } // printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size()); - m_Image->m_VtkReadersLock.Unlock(); + m_Image->m_VtkReadersLock.unlock(); } const vtkImageData *mitk::ImageVtkReadAccessor::GetVtkImageData() const { return m_ImageDataVtk; } diff --git a/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp index 8d860f03e1..15fc124e00 100644 --- a/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp +++ b/Modules/Core/src/DataManagement/mitkImageVtkWriteAccessor.cpp @@ -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. ============================================================================*/ #include "mitkImageVtkWriteAccessor.h" #include "mitkImage.h" #include const mitk::Image *mitk::ImageVtkWriteAccessor::GetImage() const { return m_Image; } mitk::ImageVtkWriteAccessor::ImageVtkWriteAccessor(ImagePointer iP, const mitk::ImageDataItem *iDI, vtkImageData *imageDataVtk) : ImageAccessorBase(nullptr, iDI), m_Image(iP.GetPointer()), m_ImageDataVtk(imageDataVtk) { - m_Image->m_VtkReadersLock.Lock(); + m_Image->m_VtkReadersLock.lock(); m_Image->m_VtkReaders.push_back(this); // printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size()); - m_Image->m_VtkReadersLock.Unlock(); + m_Image->m_VtkReadersLock.unlock(); } mitk::ImageVtkWriteAccessor::~ImageVtkWriteAccessor() { - m_Image->m_VtkReadersLock.Lock(); + m_Image->m_VtkReadersLock.lock(); auto it = std::find(m_Image->m_VtkReaders.begin(), m_Image->m_VtkReaders.end(), this); if (it != m_Image->m_VtkReaders.end()) { m_Image->m_VtkReaders.erase(it); } // printf("m_VtkReaders.size(): %d\n", (int) m_Image->m_VtkReaders.size()); - m_Image->m_VtkReadersLock.Unlock(); + m_Image->m_VtkReadersLock.unlock(); } vtkImageData *mitk::ImageVtkWriteAccessor::GetVtkImageData() const { return m_ImageDataVtk; } diff --git a/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp b/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp index c03cbe3db3..ea906399bd 100644 --- a/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp +++ b/Modules/Core/src/DataManagement/mitkImageWriteAccessor.cpp @@ -1,147 +1,147 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkImageWriteAccessor.h" mitk::ImageWriteAccessor::ImageWriteAccessor(ImagePointer image, const mitk::ImageDataItem *iDI, int OptionFlags) : ImageAccessorBase(image.GetPointer(), iDI, OptionFlags), m_Image(image) { OrganizeWriteAccess(); } mitk::ImageWriteAccessor::~ImageWriteAccessor() { // In case of non-coherent memory, copied area needs to be written back // TODO - m_Image->m_ReadWriteLock.Lock(); + m_Image->m_ReadWriteLock.lock(); // delete self from list of ImageReadAccessors in Image auto it = std::find(m_Image->m_Writers.begin(), m_Image->m_Writers.end(), this); m_Image->m_Writers.erase(it); // delete lock, if there are no waiting ImageAccessors if (m_WaitLock->m_WaiterCount <= 0) { - m_WaitLock->m_Mutex.Unlock(); + m_WaitLock->m_Mutex.unlock(); delete m_WaitLock; } else { - m_WaitLock->m_Mutex.Unlock(); + m_WaitLock->m_Mutex.unlock(); } - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); } const mitk::Image *mitk::ImageWriteAccessor::GetImage() const { return m_Image.GetPointer(); } void mitk::ImageWriteAccessor::OrganizeWriteAccess() { - m_Image->m_ReadWriteLock.Lock(); + m_Image->m_ReadWriteLock.lock(); bool readOverlap = false; bool writeOverlap = false; ImageAccessorWaitLock *overlapLock = nullptr; // Check, if there is any Read-Access going on if (m_Image->m_Readers.size() > 0) { // Check for every ReadAccessor, if the Region of this ImageAccessors overlaps // make sure this iterator is not used, when m_ReadWriteLock is Unlocked! auto it = m_Image->m_Readers.begin(); for (; it != m_Image->m_Readers.end(); ++it) { ImageAccessorBase *r = *it; if ((r->m_Options & IgnoreLock) == 0 && Overlap(r)) { // An Overlap was detected. PreventRecursiveMutexLock(r); readOverlap = true; overlapLock = r->m_WaitLock; break; } // if } // for } // if // Check, if there is any Write-Access going on if (m_Image->m_Writers.size() > 0) { // Check for every WriteAccessor, if the Region of this ImageAccessors overlaps // make sure this iterator is not used, when m_ReadWriteLock is Unlocked! auto it = m_Image->m_Writers.begin(); for (; it != m_Image->m_Writers.end(); ++it) { ImageAccessorBase *w = *it; if ((w->m_Options & IgnoreLock) == 0 && Overlap(w)) { // An Overlap was detected. PreventRecursiveMutexLock(w); // save overlapping Waitlock writeOverlap = true; overlapLock = w->m_WaitLock; break; } // if } // for } // if if (readOverlap || writeOverlap) { // Throw an exception or wait for the WriteAccessor w until it is released and start again with the request // afterwards. if (!(m_Options & ExceptionIfLocked)) { // WAIT overlapLock->m_WaiterCount += 1; - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); ImageAccessorBase::WaitForReleaseOf(overlapLock); // after waiting for the ImageAccessor, start this method again OrganizeWriteAccess(); return; } else { // THROW EXCEPTION - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); mitkThrowException(mitk::MemoryIsLockedException) << "The image part being ordered by the ImageAccessor is already in use and locked"; // MITK_ERROR("Speicherbereich belegt"); return; } } // Now, we know, that there is no conflict with a Read- or Write-Access // Lock the Mutex in ImageAccessorBase, to make sure that every other ImageAccessor has to wait - m_WaitLock->m_Mutex.Lock(); + m_WaitLock->m_Mutex.lock(); // insert self into Writers list in Image m_Image->m_Writers.push_back(this); // printf("WriteAccess %d %d\n",(int) m_Image->m_Readers.size(),(int) m_Image->m_Writers.size()); // fflush(0); - m_Image->m_ReadWriteLock.Unlock(); + m_Image->m_ReadWriteLock.unlock(); } diff --git a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp index 6da6ebc0b1..6ef243cb93 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp @@ -1,507 +1,507 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkLevelWindow.h" #include "mitkImage.h" #include "mitkImageSliceSelector.h" #include "mitkImageStatisticsHolder.h" #include void mitk::LevelWindow::EnsureConsistency() { // Check if total range is ok { if (m_RangeMin > m_RangeMax) std::swap(m_RangeMin, m_RangeMax); if (m_RangeMin == m_RangeMax) m_RangeMin = m_RangeMax - 1; } // Check if current window is ok { if (m_LowerWindowBound > m_UpperWindowBound) std::swap(m_LowerWindowBound, m_UpperWindowBound); if (m_LowerWindowBound <= m_RangeMin) m_LowerWindowBound = m_RangeMin; if (m_UpperWindowBound <= m_RangeMin) m_UpperWindowBound = m_RangeMin + 1; if (m_LowerWindowBound >= m_RangeMax) m_LowerWindowBound = m_RangeMax - 1; if (m_UpperWindowBound >= m_RangeMax) m_UpperWindowBound = m_RangeMax; if (m_LowerWindowBound == m_UpperWindowBound) { m_UpperWindowBound += 0.5; m_LowerWindowBound -= 0.5; m_UpperWindowBound = std::min(m_UpperWindowBound, m_RangeMax); m_LowerWindowBound = std::max(m_LowerWindowBound, m_RangeMin); } } } mitk::LevelWindow::LevelWindow(mitk::ScalarType level, mitk::ScalarType window) : m_LowerWindowBound(level - window / 2.0), m_UpperWindowBound(level + window / 2.0), m_RangeMin(-2048.0), m_RangeMax(4096.0), m_DefaultLowerBound(-2048.0), m_DefaultUpperBound(4096.0), m_IsFloatingImage(false), m_Fixed(false) { SetDefaultLevelWindow(level, window); SetLevelWindow(level, window, true); } mitk::LevelWindow::LevelWindow(const mitk::LevelWindow &levWin) : m_LowerWindowBound(levWin.GetLowerWindowBound()), m_UpperWindowBound(levWin.GetUpperWindowBound()), m_RangeMin(levWin.GetRangeMin()), m_RangeMax(levWin.GetRangeMax()), m_DefaultLowerBound(levWin.GetDefaultLowerBound()), m_DefaultUpperBound(levWin.GetDefaultUpperBound()), m_IsFloatingImage(levWin.IsFloatingValues()), m_Fixed(levWin.GetFixed()) { } mitk::LevelWindow::~LevelWindow() { } mitk::ScalarType mitk::LevelWindow::GetLevel() const { return (m_UpperWindowBound - m_LowerWindowBound) / 2.0 + m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetWindow() const { return (m_UpperWindowBound - m_LowerWindowBound); } mitk::ScalarType mitk::LevelWindow::GetDefaultLevel() const { return ((m_DefaultUpperBound + m_DefaultLowerBound) / 2.0); } mitk::ScalarType mitk::LevelWindow::GetDefaultWindow() const { return ((m_DefaultUpperBound - m_DefaultLowerBound)); } void mitk::LevelWindow::ResetDefaultLevelWindow() { SetLevelWindow(GetDefaultLevel(), GetDefaultWindow()); } mitk::ScalarType mitk::LevelWindow::GetLowerWindowBound() const { return m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetUpperWindowBound() const { return m_UpperWindowBound; } void mitk::LevelWindow::SetDefaultLevelWindow(mitk::ScalarType level, mitk::ScalarType window) { SetDefaultBoundaries((level - (window / 2.0)), (level + (window / 2.0))); } void mitk::LevelWindow::SetLevelWindow(mitk::ScalarType level, mitk::ScalarType window, bool expandRangesIfNecessary) { SetWindowBounds((level - (window / 2.0)), (level + (window / 2.0)), expandRangesIfNecessary); } void mitk::LevelWindow::SetWindowBounds(mitk::ScalarType lowerBound, mitk::ScalarType upperBound, bool expandRangesIfNecessary) { if (IsFixed()) return; m_LowerWindowBound = lowerBound; m_UpperWindowBound = upperBound; if (expandRangesIfNecessary) { /* if caller is sure he wants exactly that level/window, we make sure the limits match */ if (m_LowerWindowBound > m_UpperWindowBound) std::swap(m_LowerWindowBound, m_UpperWindowBound); if (m_LowerWindowBound < m_RangeMin) { m_RangeMin = m_LowerWindowBound; } if (m_UpperWindowBound > m_RangeMax) { m_RangeMax = m_UpperWindowBound; } } EnsureConsistency(); } void mitk::LevelWindow::SetRangeMinMax(mitk::ScalarType min, mitk::ScalarType max) { if (IsFixed()) return; m_RangeMin = min; m_RangeMax = max; EnsureConsistency(); } void mitk::LevelWindow::SetDefaultBoundaries(mitk::ScalarType low, mitk::ScalarType up) { if (IsFixed()) return; m_DefaultLowerBound = low; m_DefaultUpperBound = up; // Check if default window is ok { if (m_DefaultLowerBound > m_DefaultUpperBound) std::swap(m_DefaultLowerBound, m_DefaultUpperBound); if (m_DefaultLowerBound == m_DefaultUpperBound) m_DefaultLowerBound--; } EnsureConsistency(); } void mitk::LevelWindow::SetToMaxWindowSize() { SetWindowBounds(m_RangeMin, m_RangeMax); } mitk::ScalarType mitk::LevelWindow::GetRangeMin() const { return m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetRangeMax() const { return m_RangeMax; } mitk::ScalarType mitk::LevelWindow::GetRange() const { return m_RangeMax - m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetDefaultUpperBound() const { return m_DefaultUpperBound; } mitk::ScalarType mitk::LevelWindow::GetDefaultLowerBound() const { return m_DefaultLowerBound; } void mitk::LevelWindow::ResetDefaultRangeMinMax() { SetRangeMinMax(m_DefaultLowerBound, m_DefaultUpperBound); } /*! This method initializes a mitk::LevelWindow from an mitk::Image. The algorithm is as follows: Default to taking the central image slice for quick analysis. Compute the smallest (minValue), second smallest (min2ndValue), second largest (max2ndValue), and largest (maxValue) data value by traversing the pixel values only once. In the same scan it also computes the count of minValue values and maxValue values. After that a basic histogram with specific information about the extrems is complete. If minValue == maxValue, the center slice is uniform and the above scan is repeated for the complete image, not just one slice Next, special cases of images with only 1, 2 or 3 distinct data values have hand assigned level window ranges. Next the level window is set relative to the inner range IR = lengthOf([min2ndValue, max2ndValue]) For count(minValue) > 20% the smallest values are frequent and should be distinct from the min2ndValue and larger values (minValue may be std:min, may signify something special) hence the lower end of the level window is set to min2ndValue - 0.5 * IR For count(minValue) <= 20% the smallest values are not so important and can blend with the next ones => min(level window) = min2ndValue And analog for max(level window): count(max2ndValue) > 20%: max(level window) = max2ndValue + 0.5 * IR count(max2ndValue) < 20%: max(level window) = max2ndValue In both 20%+ cases the level window bounds are clamped to the [minValue, maxValue] range In consequence the level window maximizes contrast with minimal amount of computation and does do useful things if the data contains std::min or std:max values or has only 1 or 2 or 3 data values. */ void mitk::LevelWindow::SetAuto(const mitk::Image *image, bool /*tryPicTags*/, bool guessByCentralSlice, unsigned selectedComponent) { if (IsFixed()) return; if (image == nullptr || !image->IsInitialized()) return; - if (itk::ImageIOBase::IOComponentType::FLOAT == image->GetPixelType().GetComponentType() - || itk::ImageIOBase::IOComponentType::DOUBLE == image->GetPixelType().GetComponentType()) + if (itk::IOComponentEnum::FLOAT == image->GetPixelType().GetComponentType() + || itk::IOComponentEnum::DOUBLE == image->GetPixelType().GetComponentType()) { m_IsFloatingImage = true; } else { m_IsFloatingImage = false; } const mitk::Image *wholeImage = image; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); if (guessByCentralSlice) { sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); image = sliceSelector->GetOutput(); if (image == nullptr || !image->IsInitialized()) return; minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); if (minValue == maxValue) { // guessByCentralSlice seems to have failed, lets look at all data image = wholeImage; minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } } else { const_cast(image)->Update(); minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); for (unsigned int i = 1; i < image->GetDimension(3); ++i) { ScalarType minValueTemp = image->GetStatistics()->GetScalarValueMin(i, selectedComponent); if (minValue > minValueTemp) minValue = minValueTemp; ScalarType maxValueTemp = image->GetStatistics()->GetScalarValueMaxNoRecompute(i); if (maxValue < maxValueTemp) maxValue = maxValueTemp; ScalarType min2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(i); if (min2ndValue > min2ndValueTemp) min2ndValue = min2ndValueTemp; ScalarType max2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(i); if (max2ndValue > max2ndValueTemp) max2ndValue = max2ndValueTemp; } } // Fix for bug# 344 Level Window wird bei Eris Cut bildern nicht richtig gesetzt - if (image->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR && - image->GetPixelType().GetComponentType() == itk::ImageIOBase::INT && image->GetPixelType().GetBpe() >= 8) + if (image->GetPixelType().GetPixelType() == itk::IOPixelEnum::SCALAR && + image->GetPixelType().GetComponentType() == itk::IOComponentEnum::INT && image->GetPixelType().GetBpe() >= 8) { // the windows compiler complains about ambiguos 'pow' call, therefore static casting to (double, int) if (minValue == -(pow((double)2.0, static_cast(image->GetPixelType().GetBpe() / 2)))) { minValue = min2ndValue; } } // End fix //// uniform image if (minValue == maxValue) { minValue = maxValue - 1; } else { // Due to bug #8690 level window now is no longer of fixed range by default but the range adapts according to // levelwindow interaction // This is done because the range should be a little bit larger from the beginning so that the scale doesn't start // to resize right from the beginning double additionalRange = 0.15 * (maxValue - minValue); minValue -= additionalRange; maxValue += additionalRange; } if (!std::isfinite(minValue)) { minValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); } if (!std::isfinite(maxValue)) { maxValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); } SetRangeMinMax(minValue, maxValue); SetDefaultBoundaries(minValue, maxValue); size_t numPixelsInDataset = image->GetDimensions()[0]; for (decltype(image->GetDimension()) k = 1; k < image->GetDimension(); ++k) numPixelsInDataset *= image->GetDimensions()[k]; const auto minCount = image->GetStatistics()->GetCountOfMinValuedVoxelsNoRecompute(); const auto maxCount = image->GetStatistics()->GetCountOfMaxValuedVoxelsNoRecompute(); const auto minCountFraction = minCount / static_cast(numPixelsInDataset); const auto maxCountFraction = maxCount / static_cast(numPixelsInDataset); //// binary image if (min2ndValue == maxValue) { // noop; full range is fine } //// triple value image, put middle value in center of gray level ramp else if (min2ndValue == max2ndValue) { ScalarType minDelta = std::min(min2ndValue - minValue, maxValue - min2ndValue); minValue = min2ndValue - minDelta; maxValue = min2ndValue + minDelta; } // now we can assume more than three distict scalar values else { ScalarType innerRange = max2ndValue - min2ndValue; if (minCountFraction > 0.2) //// lots of min values -> make different from rest, but not miles away { ScalarType halfInnerRangeGapMinValue = min2ndValue - innerRange / 2.0; minValue = std::max(minValue, halfInnerRangeGapMinValue); } else //// few min values -> focus on innerRange { minValue = min2ndValue; } if (maxCountFraction > 0.2) //// lots of max values -> make different from rest { ScalarType halfInnerRangeGapMaxValue = max2ndValue + innerRange / 2.0; maxValue = std::min(maxValue, halfInnerRangeGapMaxValue); } else //// few max values -> focus on innerRange { maxValue = max2ndValue; } } SetWindowBounds(minValue, maxValue); SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue); } void mitk::LevelWindow::SetToImageRange(const mitk::Image *image) { if (IsFixed()) return; if (image == nullptr || !image->IsInitialized()) return; ScalarType minValue = image->GetStatistics()->GetScalarValueMin(0); if (!std::isfinite(minValue)) { minValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); } ScalarType maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0); if (!std::isfinite(maxValue)) { maxValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); } SetRangeMinMax(minValue, maxValue); SetDefaultBoundaries(minValue, maxValue); SetWindowBounds(minValue, maxValue); SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue); } void mitk::LevelWindow::SetFixed(bool fixed) { m_Fixed = fixed; } bool mitk::LevelWindow::GetFixed() const { return m_Fixed; } bool mitk::LevelWindow::IsFixed() const { return m_Fixed; } bool mitk::LevelWindow::IsFloatingValues() const { return m_IsFloatingImage; } void mitk::LevelWindow::SetFloatingValues(bool value) { m_IsFloatingImage = value; } bool mitk::LevelWindow::operator==(const mitk::LevelWindow &levWin) const { return mitk::Equal(this->m_RangeMin, levWin.m_RangeMin, mitk::sqrteps) && mitk::Equal(this->m_RangeMax, levWin.m_RangeMax, mitk::sqrteps) && mitk::Equal(this->m_DefaultLowerBound, levWin.m_DefaultLowerBound, mitk::sqrteps) && mitk::Equal(this->m_DefaultUpperBound, levWin.m_DefaultUpperBound, mitk::sqrteps) && mitk::Equal(this->m_LowerWindowBound, levWin.m_LowerWindowBound, mitk::sqrteps) && mitk::Equal(this->m_UpperWindowBound, levWin.m_UpperWindowBound, mitk::sqrteps) && m_Fixed == levWin.IsFixed() && m_IsFloatingImage == levWin.IsFloatingValues(); } bool mitk::LevelWindow::operator!=(const mitk::LevelWindow &levWin) const { return !((*this) == levWin); } mitk::LevelWindow &mitk::LevelWindow::operator=(const mitk::LevelWindow &levWin) { if (this == &levWin) { return *this; } else { m_RangeMin = levWin.GetRangeMin(); m_RangeMax = levWin.GetRangeMax(); m_LowerWindowBound = levWin.GetLowerWindowBound(); m_UpperWindowBound = levWin.GetUpperWindowBound(); m_DefaultLowerBound = levWin.GetDefaultLowerBound(); m_DefaultUpperBound = levWin.GetDefaultUpperBound(); m_Fixed = levWin.GetFixed(); m_IsFloatingImage = levWin.IsFloatingValues(); return *this; } } diff --git a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp index 4a6565d4e7..013c257025 100644 --- a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp @@ -1,971 +1,973 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkPlaneGeometry.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include "mitkPlaneOperation.h" #include #include #include namespace mitk { PlaneGeometry::PlaneGeometry() : Superclass(), m_ReferenceGeometry(nullptr) { Initialize(); } PlaneGeometry::~PlaneGeometry() {} PlaneGeometry::PlaneGeometry(const PlaneGeometry &other) : Superclass(other), m_ReferenceGeometry(other.m_ReferenceGeometry) { } bool PlaneGeometry::CheckRotationMatrix(mitk::AffineTransform3D *transform, double epsilon) { bool rotation = true; auto matrix = transform->GetMatrix().GetVnlMatrix(); matrix.normalize_columns(); auto det = vnl_determinant(matrix); if (fabs(det-1.0) > epsilon) { MITK_WARN << "Invalid rotation matrix! Determinant != 1 (" << det << ")"; rotation = false; } vnl_matrix_fixed id; id.set_identity(); auto should_be_id = matrix*matrix.transpose(); should_be_id -= id; auto max = should_be_id.absolute_value_max(); if (max > epsilon) { MITK_WARN << "Invalid rotation matrix! R*R^T != ID. Max value: " << max << " (should be 0)"; rotation = false; } return rotation; } void PlaneGeometry::CheckIndexToWorldTransform(mitk::AffineTransform3D *transform) { this->CheckRotationMatrix(transform); } void PlaneGeometry::CheckBounds(const BoundingBox::BoundsArrayType &bounds) { // error: unused parameter 'bounds' // this happens in release mode, where the assert macro is defined empty // hence we "use" the parameter: (void)bounds; // currently the unit rectangle must be starting at the origin [0,0] assert(bounds[0] == 0); assert(bounds[2] == 0); // the unit rectangle must be two-dimensional assert(bounds[1] > 0); assert(bounds[3] > 0); } void PlaneGeometry::IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const { pt_mm[0] = GetExtentInMM(0) / GetExtent(0) * pt_units[0]; pt_mm[1] = GetExtentInMM(1) / GetExtent(1) * pt_units[1]; } void PlaneGeometry::WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const { pt_units[0] = pt_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0))); pt_units[1] = pt_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1))); } void PlaneGeometry::IndexToWorld(const Point2D & /*atPt2d_units*/, const Vector2D &vec_units, Vector2D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::IndexToWorld(point, vec, vec). Use " "PlaneGeometry::IndexToWorld(vec, vec) instead!"; this->IndexToWorld(vec_units, vec_mm); } void PlaneGeometry::IndexToWorld(const Vector2D &vec_units, Vector2D &vec_mm) const { vec_mm[0] = (GetExtentInMM(0) / GetExtent(0)) * vec_units[0]; vec_mm[1] = (GetExtentInMM(1) / GetExtent(1)) * vec_units[1]; } void PlaneGeometry::WorldToIndex(const Point2D & /*atPt2d_mm*/, const Vector2D &vec_mm, Vector2D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function PlaneGeometry::WorldToIndex(point, vec, vec). Use " "PlaneGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } void PlaneGeometry::WorldToIndex(const Vector2D &vec_mm, Vector2D &vec_units) const { vec_units[0] = vec_mm[0] * (1.0 / (GetExtentInMM(0) / GetExtent(0))); vec_units[1] = vec_mm[1] * (1.0 / (GetExtentInMM(1) / GetExtent(1))); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const Vector3D &spacing, PlaneGeometry::PlaneOrientation planeorientation, ScalarType zPosition, bool frontside, bool rotated, bool top) { AffineTransform3D::Pointer transform; transform = AffineTransform3D::New(); AffineTransform3D::MatrixType matrix; AffineTransform3D::MatrixType::InternalMatrixType &vnlmatrix = matrix.GetVnlMatrix(); vnlmatrix.set_identity(); vnlmatrix(0, 0) = spacing[0]; vnlmatrix(1, 1) = spacing[1]; vnlmatrix(2, 2) = spacing[2]; transform->SetIdentity(); transform->SetMatrix(matrix); InitializeStandardPlane(width, height, transform.GetPointer(), planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, mitk::ScalarType height, const AffineTransform3D *transform /* = nullptr */, PlaneGeometry::PlaneOrientation planeorientation /* = Axial */, mitk::ScalarType zPosition /* = 0 */, bool frontside /* = true */, bool rotated /* = false */, bool top /* = true */) { Superclass::Initialize(); /// construct standard view. // We define at the moment "frontside" as: axial from above, // coronal from front (nose), saggital from right. // TODO: Double check with medicals doctors or radiologists [ ]. // We define the orientation in patient's view, e.g. LAI is in a axial cut // (parallel to the triangle ear-ear-nose): // first axis: To the left ear of the patient // seecond axis: To the nose of the patient // third axis: To the legs of the patient. // Options are: L/R left/right; A/P anterior/posterior; I/S inferior/superior // (AKA caudal/cranial). // We note on all cases in the following switch block r.h. for right handed // or l.h. for left handed to describe the different cases. // However, which system is chosen is defined at the end of the switch block. // CAVE / be careful: the vectors right and bottom are relative to the plane // and do NOT describe e.g. the right side of the patient. Point3D origin; /** Bottom means downwards, DV means Direction Vector. Both relative to the image! */ VnlVector rightDV(3), bottomDV(3); /** Origin of this plane is by default a zero vector and implicitly in the top-left corner: */ origin.Fill(0); /** This is different to all definitions in MITK, except the QT mouse clicks. * But it is like this here and we don't want to change a running system. * Just be aware, that IN THIS FUNCTION we define the origin at the top left (e.g. your screen). */ /** NormalDirection defines which axis (i.e. column index in the transform matrix) * is perpendicular to the plane: */ int normalDirection; switch (planeorientation) // Switch through our limited choice of standard planes: { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: if (frontside) // Radiologist's view from below. A cut along the triangle ear-ear-nose. { if (rotated == false) /** Origin in the top-left corner, x=[1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], * origin=[0,0,zpos]: LAI (r.h.) * * 0---rightDV----> | * | | * | Picture of a finite, rectangular plane | * | ( insert LOLCAT-scan here ^_^ ) | * | | * v _________________________________________| * */ { FillVector3D(origin, 0, 0, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else // Origin rotated to the bottom-right corner, x=[-1; 0; 0], y=[0; -1; 0], z=[0; 0; 1], // origin=[w,h,zpos]: RPI (r.h.) { // Caveat emptor: Still using top-left as origin of index coordinate system! FillVector3D(origin, width, height, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } else // 'Backside, not frontside.' Neuro-Surgeons's view from above patient. { if (rotated == false) // x=[-1; 0; 0], y=[0; 1; 0], z=[0; 0; 1], origin=[w,0,zpos]: RAS (r.h.) { FillVector3D(origin, width, 0, zPosition); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 1, 0); } else // Origin in the bottom-left corner, x=[1; 0; 0], y=[0; -1; 0], z=[0; 0; 1], // origin=[0,h,zpos]: LPS (r.h.) { FillVector3D(origin, 0, height, zPosition); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, -1, 0); } } normalDirection = 2; // That is S=Superior=z=third_axis=middlefinger in righthanded LPS-system. break; // Frontal is known as Coronal in mitk. Plane cuts through patient's ear-ear-heel-heel: case Frontal: if (frontside) { if (rotated == false) // x=[1; 0; 0], y=[0; 0; 1], z=[0; 1; 0], origin=[0,zpos,0]: LAI (r.h.) { FillVector3D(origin, 0, zPosition, 0); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[-1;0;0], y=[0;0;-1], z=[0;1;0], origin=[w,zpos,h]: RAS (r.h.) { FillVector3D(origin, width, zPosition, height); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if (rotated == false) // x=[-1;0;0], y=[0;0;1], z=[0;1;0], origin=[w,zpos,0]: RPI (r.h.) { FillVector3D(origin, width, zPosition, 0); FillVector3D(rightDV, -1, 0, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[1;0;0], y=[0;1;0], z=[0;0;-1], origin=[0,zpos,h]: LPS (r.h.) { FillVector3D(origin, 0, zPosition, height); FillVector3D(rightDV, 1, 0, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 1; // Normal vector = posterior direction. break; case Sagittal: // Sagittal=Medial plane, the symmetry-plane mirroring your face. if (frontside) { if (rotated == false) // x=[0;1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,0,0]: LAI (r.h.) { FillVector3D(origin, zPosition, 0, 0); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[0;-1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,w,h]: LPS (r.h.) { FillVector3D(origin, zPosition, width, height); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, -1); } } else { if (rotated == false) // x=[0;-1;0], y=[0;0;1], z=[1;0;0], origin=[zpos,w,0]: RPI (r.h.) { FillVector3D(origin, zPosition, width, 0); FillVector3D(rightDV, 0, -1, 0); FillVector3D(bottomDV, 0, 0, 1); } else // x=[0;1;0], y=[0;0;-1], z=[1;0;0], origin=[zpos,0,h]: RAS (r.h.) { FillVector3D(origin, zPosition, 0, height); FillVector3D(rightDV, 0, 1, 0); FillVector3D(bottomDV, 0, 0, -1); } } normalDirection = 0; // Normal vector = Lateral direction: Left in a LPS-system. break; default: itkExceptionMacro("unknown PlaneOrientation"); } VnlVector normal(3); FillVector3D(normal, 0, 0, 0); normal[normalDirection] = top ? 1 : -1; if ( transform != nullptr ) { origin = transform->TransformPoint( origin ); - rightDV = transform->TransformVector( rightDV ); - bottomDV = transform->TransformVector( bottomDV ); - normal = transform->TransformVector( normal ); + rightDV = transform->TransformVector( rightDV ).as_ref(); + bottomDV = transform->TransformVector( bottomDV ).as_ref(); + normal = transform->TransformVector( normal ).as_ref(); } ScalarType bounds[6] = {0, width, 0, height, 0, 1}; this->SetBounds(bounds); AffineTransform3D::Pointer planeTransform = AffineTransform3D::New(); Matrix3D matrix; - matrix.GetVnlMatrix().set_column(0, rightDV); - matrix.GetVnlMatrix().set_column(1, bottomDV); - matrix.GetVnlMatrix().set_column(2, normal); + matrix.GetVnlMatrix().set_column(0, rightDV.as_ref()); + matrix.GetVnlMatrix().set_column(1, bottomDV.as_ref()); + matrix.GetVnlMatrix().set_column(2, normal.as_ref()); planeTransform->SetMatrix(matrix); planeTransform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); this->SetIndexToWorldTransform(planeTransform); this->SetOrigin(origin); } std::vector< int > PlaneGeometry::CalculateDominantAxes(mitk::AffineTransform3D::MatrixType::InternalMatrixType& rotation_matrix) { std::vector< int > axes; bool dominant_axis_error = false; for (int i = 0; i < 3; ++i) { int dominantAxis = itk::Function::Max3( rotation_matrix[0][i], rotation_matrix[1][i], rotation_matrix[2][i] ); for (int j=0; jSetReferenceGeometry(geometry3D); ScalarType width, height; // Inspired by: // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3 mitk::AffineTransform3D::MatrixType matrix = geometry3D->GetIndexToWorldTransform()->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetTranspose(); /// The index of the sagittal, coronal and axial axes in the reference geometry. auto axes = CalculateDominantAxes(inverseMatrix); /// The direction of the sagittal, coronal and axial axes in the reference geometry. /// +1 means that the direction is straight, i.e. greater index translates to greater /// world coordinate. -1 means that the direction is inverted. int directions[3]; ScalarType extents[3]; ScalarType spacings[3]; for (int i=0; i<3; ++i) { int dominantAxis = axes.at(i); directions[i] = itk::Function::Sign(inverseMatrix[dominantAxis][i]); extents[i] = geometry3D->GetExtent(dominantAxis); spacings[i] = geometry3D->GetSpacing()[dominantAxis]; } // matrix(column) = inverseTransformMatrix(row) * flippedAxes * spacing matrix[0][0] = inverseMatrix[axes[0]][0] * directions[0] * spacings[0]; matrix[1][0] = inverseMatrix[axes[0]][1] * directions[0] * spacings[0]; matrix[2][0] = inverseMatrix[axes[0]][2] * directions[0] * spacings[0]; matrix[0][1] = inverseMatrix[axes[1]][0] * directions[1] * spacings[1]; matrix[1][1] = inverseMatrix[axes[1]][1] * directions[1] * spacings[1]; matrix[2][1] = inverseMatrix[axes[1]][2] * directions[1] * spacings[1]; matrix[0][2] = inverseMatrix[axes[2]][0] * directions[2] * spacings[2]; matrix[1][2] = inverseMatrix[axes[2]][1] * directions[2] * spacings[2]; matrix[2][2] = inverseMatrix[axes[2]][2] * directions[2] * spacings[2]; /// The "world origin" is the corner with the lowest physical coordinates. /// We use it as a reference point so that we get the correct anatomical /// orientations. Point3D worldOrigin = geometry3D->GetOrigin(); for (int i = 0; i < 3; ++i) { /// The distance of the plane origin from the world origin in voxels. double offset = directions[i] > 0 ? 0.0 : extents[i]; if (geometry3D->GetImageGeometry()) { offset += directions[i] * 0.5; } for (int j = 0; j < 3; ++j) { worldOrigin[j] -= offset * matrix[j][i]; } } switch(planeorientation) { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: width = extents[0]; height = extents[1]; break; case Frontal: width = extents[0]; height = extents[2]; break; case Sagittal: width = extents[1]; height = extents[2]; break; default: itkExceptionMacro("unknown PlaneOrientation"); } ScalarType bounds[6]= { 0, width, 0, height, 0, 1 }; this->SetBounds( bounds ); AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(matrix); transform->SetOffset(worldOrigin.GetVectorFromOrigin()); InitializeStandardPlane( width, height, transform, planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane( const BaseGeometry *geometry3D, bool top, PlaneOrientation planeorientation, bool frontside, bool rotated) { /// The index of the sagittal, coronal and axial axes in world coordinate system. int worldAxis; switch(planeorientation) { case None: /** Orientation 'None' shall be done like the axial plane orientation, * for whatever reasons. */ case Axial: worldAxis = 2; break; case Frontal: worldAxis = 1; break; case Sagittal: worldAxis = 0; break; default: itkExceptionMacro("unknown PlaneOrientation"); } // Inspired by: // http://www.na-mic.org/Wiki/index.php/Coordinate_System_Conversion_Between_ITK_and_Slicer3 mitk::AffineTransform3D::ConstPointer affineTransform = geometry3D->GetIndexToWorldTransform(); mitk::AffineTransform3D::MatrixType matrix = affineTransform->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); /// The index of the sagittal, coronal and axial axes in the reference geometry. int dominantAxis = CalculateDominantAxes(inverseMatrix).at(worldAxis); ScalarType zPosition = top ? 0.5 : geometry3D->GetExtent(dominantAxis) - 0.5; InitializeStandardPlane(geometry3D, planeorientation, zPosition, frontside, rotated, top); } void PlaneGeometry::InitializeStandardPlane(const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing) { InitializeStandardPlane(rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing); } void PlaneGeometry::InitializeStandardPlane(const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing) { ScalarType width = rightVector.two_norm(); ScalarType height = downVector.two_norm(); InitializeStandardPlane(width, height, rightVector, downVector, spacing); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing) { InitializeStandardPlane(width, height, rightVector.GetVnlVector(), downVector.GetVnlVector(), spacing); } void PlaneGeometry::InitializeStandardPlane(mitk::ScalarType width, ScalarType height, const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing) { assert(width > 0); assert(height > 0); VnlVector rightDV = rightVector; rightDV.normalize(); VnlVector downDV = downVector; downDV.normalize(); VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); // Crossproduct vnl_cross_3d is always righthanded, but that is okay here // because in this method we create a new IndexToWorldTransform and // spacing with 1 or 3 negative components could still make it lefthanded. if (spacing != nullptr) { rightDV *= (*spacing)[0]; downDV *= (*spacing)[1]; normal *= (*spacing)[2]; } AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightDV); matrix.GetVnlMatrix().set_column(1, downDV); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); ScalarType bounds[6] = {0, width, 0, height, 0, 1}; this->SetBounds(bounds); this->SetIndexToWorldTransform(transform); } void PlaneGeometry::InitializePlane(const Point3D &origin, const Vector3D &normal) { VnlVector rightVectorVnl(3), downVectorVnl; if (Equal(normal[1], 0.0f) == false) { FillVector3D(rightVectorVnl, 1.0f, -normal[0] / normal[1], 0.0f); rightVectorVnl.normalize(); } else { FillVector3D(rightVectorVnl, 0.0f, 1.0f, 0.0f); } downVectorVnl = vnl_cross_3d(normal.GetVnlVector(), rightVectorVnl); downVectorVnl.normalize(); // Crossproduct vnl_cross_3d is always righthanded. InitializeStandardPlane(rightVectorVnl, downVectorVnl); SetOrigin(origin); } void PlaneGeometry::SetMatrixByVectors(const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness /* = 1.0 */) { VnlVector normal = vnl_cross_3d(rightVector, downVector); normal.normalize(); normal *= thickness; // Crossproduct vnl_cross_3d is always righthanded, but that is okay here // because in this method we create a new IndexToWorldTransform and // a negative thickness could still make it lefthanded. AffineTransform3D::Pointer transform = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightVector); matrix.GetVnlMatrix().set_column(1, downVector); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(this->GetIndexToWorldTransform()->GetOffset()); SetIndexToWorldTransform(transform); } Vector3D PlaneGeometry::GetNormal() const { Vector3D frontToBack; - frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); + frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref()); return frontToBack; } VnlVector PlaneGeometry::GetNormalVnl() const { - return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2); + return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref(); } ScalarType PlaneGeometry::DistanceFromPlane(const Point3D &pt3d_mm) const { return fabs(SignedDistance(pt3d_mm)); } ScalarType PlaneGeometry::SignedDistance(const Point3D &pt3d_mm) const { return SignedDistanceFromPlane(pt3d_mm); } bool PlaneGeometry::IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox) const { if (considerBoundingBox) { Point3D pt3d_units; BaseGeometry::WorldToIndex(pt3d_mm, pt3d_units); return (pt3d_units[2] > this->GetBoundingBox()->GetBounds()[4]); } else return SignedDistanceFromPlane(pt3d_mm) > 0; } - bool PlaneGeometry::IntersectionLine(const PlaneGeometry *plane, Line3D &crossline) const + bool PlaneGeometry::IntersectionLine(const PlaneGeometry* plane, Line3D& crossline) const { Vector3D normal = this->GetNormal(); normal.Normalize(); Vector3D planeNormal = plane->GetNormal(); planeNormal.Normalize(); Vector3D direction = itk::CrossProduct(normal, planeNormal); if (direction.GetSquaredNorm() < eps) return false; crossline.SetDirection(direction); double N1dN2 = normal * planeNormal; double determinant = 1.0 - N1dN2 * N1dN2; Vector3D origin = this->GetOrigin().GetVectorFromOrigin(); Vector3D planeOrigin = plane->GetOrigin().GetVectorFromOrigin(); double d1 = normal * origin; double d2 = planeNormal * planeOrigin; double c1 = (d1 - d2 * N1dN2) / determinant; double c2 = (d2 - d1 * N1dN2) / determinant; Vector3D p = normal * c1 + planeNormal * c2; - crossline.GetPoint().GetVnlVector() = p.GetVnlVector(); + crossline.GetPoint()[0] = p.GetVnlVector()[0]; + crossline.GetPoint()[1] = p.GetVnlVector()[1]; + crossline.GetPoint()[2] = p.GetVnlVector()[2]; return true; } unsigned int PlaneGeometry::IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo) const { Line3D crossline; if (this->IntersectionLine(plane, crossline) == false) return 0; Point2D point2; Vector2D direction2; this->Map(crossline.GetPoint(), point2); this->Map(crossline.GetPoint(), crossline.GetDirection(), direction2); return Line3D::RectangleLineIntersection( 0, 0, GetExtentInMM(0), GetExtentInMM(1), point2, direction2, lineFrom, lineTo); } double PlaneGeometry::Angle(const PlaneGeometry *plane) const { return angle(plane->GetMatrixColumn(2), GetMatrixColumn(2)); } double PlaneGeometry::Angle(const Line3D &line) const { return vnl_math::pi_over_2 - angle(line.GetDirection().GetVnlVector(), GetMatrixColumn(2)); } bool PlaneGeometry::IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const { Vector3D planeNormal = this->GetNormal(); planeNormal.Normalize(); Vector3D lineDirection = line.GetDirection(); lineDirection.Normalize(); double t = planeNormal * lineDirection; if (fabs(t) < eps) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = (planeNormal * diff) / t; intersectionPoint = line.GetPoint() + lineDirection * t; return true; } bool PlaneGeometry::IntersectionPointParam(const Line3D &line, double &t) const { Vector3D planeNormal = this->GetNormal(); Vector3D lineDirection = line.GetDirection(); t = planeNormal * lineDirection; if (fabs(t) < eps) { return false; } Vector3D diff; diff = this->GetOrigin() - line.GetPoint(); t = (planeNormal * diff) / t; return true; } bool PlaneGeometry::IsParallel(const PlaneGeometry *plane) const { return ((Angle(plane) < 10.0 * mitk::sqrteps) || (Angle(plane) > (vnl_math::pi - 10.0 * sqrteps))); } bool PlaneGeometry::IsOnPlane(const Point3D &point) const { return Distance(point) < eps; } bool PlaneGeometry::IsOnPlane(const Line3D &line) const { return ((Distance(line.GetPoint()) < eps) && (Distance(line.GetPoint2()) < eps)); } bool PlaneGeometry::IsOnPlane(const PlaneGeometry *plane) const { return (IsParallel(plane) && (Distance(plane->GetOrigin()) < eps)); } Point3D PlaneGeometry::ProjectPointOntoPlane(const Point3D &pt) const { ScalarType len = this->GetNormalVnl().two_norm(); return pt - this->GetNormal() * this->SignedDistanceFromPlane(pt) / len; } itk::LightObject::Pointer PlaneGeometry::InternalClone() const { Self::Pointer newGeometry = new PlaneGeometry(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void PlaneGeometry::ExecuteOperation(Operation *operation) { vtkTransform *transform = vtkTransform::New(); transform->SetMatrix(this->GetVtkMatrix()); switch (operation->GetOperationType()) { case OpORIENT: { auto *planeOp = dynamic_cast(operation); if (planeOp == nullptr) { return; } Point3D center = planeOp->GetPoint(); Vector3D orientationVector = planeOp->GetNormal(); Vector3D defaultVector; FillVector3D(defaultVector, 0.0, 0.0, 1.0); Vector3D rotationAxis = itk::CrossProduct(orientationVector, defaultVector); // double rotationAngle = acos( orientationVector[2] / orientationVector.GetNorm() ); double rotationAngle = atan2((double)rotationAxis.GetNorm(), (double)(orientationVector * defaultVector)); rotationAngle *= 180.0 / vnl_math::pi; transform->PostMultiply(); transform->Identity(); transform->Translate(center[0], center[1], center[2]); transform->RotateWXYZ(rotationAngle, rotationAxis[0], rotationAxis[1], rotationAxis[2]); transform->Translate(-center[0], -center[1], -center[2]); break; } case OpRESTOREPLANEPOSITION: { auto *op = dynamic_cast(operation); if (op == nullptr) { return; } AffineTransform3D::Pointer transform2 = AffineTransform3D::New(); Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(0)); matrix.GetVnlMatrix().set_column(1, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(1)); matrix.GetVnlMatrix().set_column(2, op->GetTransform()->GetMatrix().GetVnlMatrix().get_column(2)); transform2->SetMatrix(matrix); Vector3D offset = op->GetTransform()->GetOffset(); transform2->SetOffset(offset); this->SetIndexToWorldTransform(transform2); ScalarType bounds[6] = {0, op->GetWidth(), 0, op->GetHeight(), 0, 1}; this->SetBounds(bounds); this->Modified(); transform->Delete(); return; } default: Superclass::ExecuteOperation(operation); transform->Delete(); return; } this->SetVtkMatrixDeepCopy(transform); this->Modified(); transform->Delete(); } void PlaneGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << " ScaleFactorMMPerUnitX: " << GetExtentInMM(0) / GetExtent(0) << std::endl; os << indent << " ScaleFactorMMPerUnitY: " << GetExtentInMM(1) / GetExtent(1) << std::endl; os << indent << " Normal: " << GetNormal() << std::endl; } bool PlaneGeometry::Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const { assert(this->IsBoundingBoxNull() == false); Point3D pt3d_units; Superclass::WorldToIndex(pt3d_mm, pt3d_units); pt2d_mm[0] = pt3d_units[0] * GetExtentInMM(0) / GetExtent(0); pt2d_mm[1] = pt3d_units[1] * GetExtentInMM(1) / GetExtent(1); pt3d_units[2] = 0; return this->GetBoundingBox()->IsInside(pt3d_units); } void PlaneGeometry::Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const { // pt2d_mm is measured from the origin of the world geometry (at leats it called form BaseRendere::Mouse...Event) Point3D pt3d_units; pt3d_units[0] = pt2d_mm[0] / (GetExtentInMM(0) / GetExtent(0)); pt3d_units[1] = pt2d_mm[1] / (GetExtentInMM(1) / GetExtent(1)); pt3d_units[2] = 0; // pt3d_units is a continuos index. We divided it with the Scale Factor (= spacing in x and y) to convert it from mm // to index units. // pt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); // now we convert the 3d index to a 3D world point in mm. We could have used IndexToWorld as well as // GetITW->Transform... } void PlaneGeometry::SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height) { ScalarType bounds[6] = {0, width, 0, height, 0, 1}; ScalarType extent, newextentInMM; if (GetExtent(0) > 0) { extent = GetExtent(0); if (width > extent) newextentInMM = GetExtentInMM(0) / width * extent; else newextentInMM = GetExtentInMM(0) * extent / width; SetExtentInMM(0, newextentInMM); } if (GetExtent(1) > 0) { extent = GetExtent(1); if (width > extent) newextentInMM = GetExtentInMM(1) / height * extent; else newextentInMM = GetExtentInMM(1) * extent / height; SetExtentInMM(1, newextentInMM); } SetBounds(bounds); } bool PlaneGeometry::Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const { assert(this->IsBoundingBoxNull() == false); Point3D pt3d_units; Superclass::WorldToIndex(pt3d_mm, pt3d_units); pt3d_units[2] = 0; projectedPt3d_mm = GetIndexToWorldTransform()->TransformPoint(pt3d_units); return this->GetBoundingBox()->IsInside(pt3d_units); } bool PlaneGeometry::Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { assert(this->IsBoundingBoxNull() == false); Vector3D vec3d_units; Superclass::WorldToIndex(vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); return true; } bool PlaneGeometry::Project(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const { MITK_WARN << "Deprecated function! Call Project(vec3D,vec3D) instead."; assert(this->IsBoundingBoxNull() == false); Vector3D vec3d_units; Superclass::WorldToIndex(atPt3d_mm, vec3d_mm, vec3d_units); vec3d_units[2] = 0; projectedVec3d_mm = GetIndexToWorldTransform()->TransformVector(vec3d_units); Point3D pt3d_units; Superclass::WorldToIndex(atPt3d_mm, pt3d_units); return this->GetBoundingBox()->IsInside(pt3d_units); } bool PlaneGeometry::Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const { Point2D pt2d_mm_start, pt2d_mm_end; Point3D pt3d_mm_end; bool inside = Map(atPt3d_mm, pt2d_mm_start); pt3d_mm_end = atPt3d_mm + vec3d_mm; inside &= Map(pt3d_mm_end, pt2d_mm_end); vec2d_mm = pt2d_mm_end - pt2d_mm_start; return inside; } void PlaneGeometry::Map(const mitk::Point2D & /*atPt2d_mm*/, const mitk::Vector2D & /*vec2d_mm*/, mitk::Vector3D & /*vec3d_mm*/) const { //@todo implement parallel to the other Map method! assert(false); } void PlaneGeometry::SetReferenceGeometry(const mitk::BaseGeometry *geometry) { m_ReferenceGeometry = geometry; } const mitk::BaseGeometry *PlaneGeometry::GetReferenceGeometry() const { return m_ReferenceGeometry; } bool PlaneGeometry::HasReferenceGeometry() const { return (m_ReferenceGeometry != nullptr); } } // namespace diff --git a/Modules/Core/src/DataManagement/mitkPointSet.cpp b/Modules/Core/src/DataManagement/mitkPointSet.cpp index e8294bb02b..024ad92dec 100755 --- a/Modules/Core/src/DataManagement/mitkPointSet.cpp +++ b/Modules/Core/src/DataManagement/mitkPointSet.cpp @@ -1,955 +1,965 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPointSet.h" #include "mitkInteractionConst.h" #include "mitkPointOperation.h" #include #include +namespace mitk +{ + itkEventMacroDefinition(PointSetEvent, itk::AnyEvent); + itkEventMacroDefinition(PointSetMoveEvent, PointSetEvent); + itkEventMacroDefinition(PointSetSizeChangeEvent, PointSetEvent); + itkEventMacroDefinition(PointSetAddEvent, PointSetSizeChangeEvent); + itkEventMacroDefinition(PointSetRemoveEvent, PointSetSizeChangeEvent); + itkEventMacroDefinition(PointSetExtendTimeRangeEvent, PointSetEvent); +} + mitk::PointSet::PointSet() : m_CalculateBoundingBox(true) { this->InitializeEmpty(); } mitk::PointSet::PointSet(const PointSet &other) : BaseData(other), m_PointSetSeries(other.GetPointSetSeriesSize()), m_CalculateBoundingBox(true) { // Copy points for (std::size_t t = 0; t < m_PointSetSeries.size(); ++t) { m_PointSetSeries[t] = DataType::New(); DataType::Pointer otherPts = other.GetPointSet(t); for (PointsConstIterator i = other.Begin(t); i != other.End(t); ++i) { m_PointSetSeries[t]->SetPoint(i.Index(), i.Value()); PointDataType pointData; if (otherPts->GetPointData(i.Index(), &pointData)) { m_PointSetSeries[t]->SetPointData(i.Index(), pointData); } } } } mitk::PointSet::~PointSet() { this->ClearData(); } void mitk::PointSet::ClearData() { m_PointSetSeries.clear(); Superclass::ClearData(); } void mitk::PointSet::InitializeEmpty() { m_PointSetSeries.resize(1); m_PointSetSeries[0] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[0]->SetPointData(pointData); m_CalculateBoundingBox = false; Superclass::InitializeTimeGeometry(1); m_Initialized = true; m_EmptyPointsContainer = DataType::PointsContainer::New(); } bool mitk::PointSet::IsEmptyTimeStep(unsigned int t) const { return IsInitialized() && (GetSize(t) == 0); } void mitk::PointSet::Expand(unsigned int timeSteps) { // Check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient pre-initialized // elements. // // NOTE: This method will never REDUCE the vector size; it should only // be used to make sure that the vector has enough elements to include the // specified time step. unsigned int oldSize = m_PointSetSeries.size(); if (timeSteps > oldSize) { Superclass::Expand(timeSteps); m_PointSetSeries.resize(timeSteps); for (unsigned int i = oldSize; i < timeSteps; ++i) { m_PointSetSeries[i] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[i]->SetPointData(pointData); } // if the size changes, then compute the bounding box m_CalculateBoundingBox = true; this->InvokeEvent(PointSetExtendTimeRangeEvent()); } } unsigned int mitk::PointSet::GetPointSetSeriesSize() const { return m_PointSetSeries.size(); } int mitk::PointSet::GetSize(unsigned int t) const { if (t < m_PointSetSeries.size()) { return m_PointSetSeries[t]->GetNumberOfPoints(); } else { return 0; } } mitk::PointSet::DataType::Pointer mitk::PointSet::GetPointSet(int t) const { if (t < (int)m_PointSetSeries.size()) { return m_PointSetSeries[t]; } else { return nullptr; } } mitk::PointSet::PointsIterator mitk::PointSet::Begin(int t) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsConstIterator mitk::PointSet::Begin(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsIterator mitk::PointSet::End(int t) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsConstIterator mitk::PointSet::End(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsIterator mitk::PointSet::GetMaxId(int t) { if ((unsigned int)t >= m_PointSetSeries.size()) { return m_EmptyPointsContainer->End(); } return this->Begin(t) == this->End(t) ? this->End(t) : --End(t); } int mitk::PointSet::SearchPoint(Point3D point, ScalarType distance, int t) const { if (t >= (int)m_PointSetSeries.size()) { return -1; } // Out is the point which is checked to be the searched point PointType out; out.Fill(0); PointType indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); // Searching the first point in the Set, that is +- distance far away fro // the given point unsigned int i; PointsContainer::Iterator it, end; end = m_PointSetSeries[t]->GetPoints()->End(); int bestIndex = -1; distance = distance * distance; // To correct errors from converting index to world and world to index if (distance == 0.0) { distance = 0.000001; } ScalarType bestDist = distance; ScalarType dist, tmp; for (it = m_PointSetSeries[t]->GetPoints()->Begin(), i = 0; it != end; ++it, ++i) { bool ok = m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(it->Index(), &out); if (!ok) { return -1; } else if (indexPoint == out) // if totally equal { return it->Index(); } // distance calculation tmp = out[0] - indexPoint[0]; dist = tmp * tmp; tmp = out[1] - indexPoint[1]; dist += tmp * tmp; tmp = out[2] - indexPoint[2]; dist += tmp * tmp; if (dist < bestDist) { bestIndex = it->Index(); bestDist = dist; } } return bestIndex; } mitk::PointSet::PointType mitk::PointSet::GetPoint(PointIdentifier id, int t) const { PointType out; out.Fill(0); if ((unsigned int)t >= m_PointSetSeries.size()) { return out; } if (m_PointSetSeries[t]->GetPoints()->IndexExists(id)) { m_PointSetSeries[t]->GetPoint(id, &out); this->GetGeometry(t)->IndexToWorld(out, out); return out; } else { return out; } } bool mitk::PointSet::GetPointIfExists(PointIdentifier id, PointType *point, int t) const { if ((unsigned int)t >= m_PointSetSeries.size()) { return false; } if (m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(id, point)) { this->GetGeometry(t)->IndexToWorld(*point, *point); return true; } else { return false; } } void mitk::PointSet::SetPoint(PointIdentifier id, PointType point, int t) { // Adapt the size of the data vector if necessary this->Expand(t + 1); mitk::Point3D indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->SetPoint(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::SetPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t) { // Adapt the size of the data vector if necessary this->Expand(t + 1); mitk::Point3D indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->SetPoint(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->SetPointData(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::InsertPoint(PointIdentifier id, PointType point, int t) { this->InsertPoint(id, point, mitk::PTUNDEFINED, t); } void mitk::PointSet::InsertPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t) { if ((unsigned int)t < m_PointSetSeries.size()) { mitk::Point3D indexPoint; mitk::BaseGeometry *tempGeometry = this->GetGeometry(t); if (tempGeometry == nullptr) { MITK_INFO << __FILE__ << ", l." << __LINE__ << ": GetGeometry of " << t << " returned nullptr!" << std::endl; return; } tempGeometry->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->GetPoints()->InsertElement(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->GetPointData()->InsertElement(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } } mitk::PointSet::PointIdentifier mitk::PointSet::InsertPoint(PointType point, int t) { // Adapt the size of the data vector if necessary this->Expand(t + 1); PointIdentifier id = 0; if (m_PointSetSeries[t]->GetNumberOfPoints() > 0) { PointsIterator it = --End(t); id = it.Index(); ++id; } mitk::Point3D indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->SetPoint(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); return id; } bool mitk::PointSet::RemovePointIfExists(PointIdentifier id, int t) { if ((unsigned int)t < m_PointSetSeries.size()) { DataType *pointSet = m_PointSetSeries[t]; PointsContainer *points = pointSet->GetPoints(); PointDataContainer *pdata = pointSet->GetPointData(); bool exists = points->IndexExists(id); if (exists) { points->DeleteIndex(id); pdata->DeleteIndex(id); return true; } } return false; } mitk::PointSet::PointsIterator mitk::PointSet::RemovePointAtEnd(int t) { if ((unsigned int)t < m_PointSetSeries.size()) { DataType *pointSet = m_PointSetSeries[t]; PointsContainer *points = pointSet->GetPoints(); PointDataContainer *pdata = pointSet->GetPointData(); PointsIterator bit = points->Begin(); PointsIterator eit = points->End(); if (eit != bit) { PointsContainer::ElementIdentifier id = (--eit).Index(); points->DeleteIndex(id); pdata->DeleteIndex(id); PointsIterator eit2 = points->End(); return --eit2; } else { return eit; } } return m_EmptyPointsContainer->End(); } bool mitk::PointSet::SwapPointPosition(PointIdentifier id, bool moveUpwards, int t) { if (IndexExists(id, t)) { PointType point = GetPoint(id, t); if (moveUpwards) { // up if (IndexExists(id - 1, t)) { InsertPoint(id, GetPoint(id - 1, t), t); InsertPoint(id - 1, point, t); this->Modified(); return true; } } else { // down if (IndexExists(id + 1, t)) { InsertPoint(id, GetPoint(id + 1, t), t); InsertPoint(id + 1, point, t); this->Modified(); return true; } } } return false; } bool mitk::PointSet::IndexExists(int position, int t) const { if ((unsigned int)t < m_PointSetSeries.size()) { return m_PointSetSeries[t]->GetPoints()->IndexExists(position); } else { return false; } } bool mitk::PointSet::GetSelectInfo(int position, int t) const { if (this->IndexExists(position, t)) { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[t]->GetPointData(position, &pointData); return pointData.selected; } else { return false; } } void mitk::PointSet::SetSelectInfo(int position, bool selected, int t) { if (this->IndexExists(position, t)) { // timeStep to ms TimePointType timeInMS = this->GetTimeGeometry()->TimeStepToTimePoint(t); // point Point3D point = this->GetPoint(position, t); std::unique_ptr op; if (selected) { op.reset(new mitk::PointOperation(OpSELECTPOINT, timeInMS, point, position)); } else { op.reset(new mitk::PointOperation(OpDESELECTPOINT, timeInMS, point, position)); } this->ExecuteOperation(op.get()); } } mitk::PointSpecificationType mitk::PointSet::GetSpecificationTypeInfo(int position, int t) const { if (this->IndexExists(position, t)) { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[t]->GetPointData(position, &pointData); return pointData.pointSpec; } else { return PTUNDEFINED; } } int mitk::PointSet::GetNumberOfSelected(int t) const { if ((unsigned int)t >= m_PointSetSeries.size()) { return 0; } int numberOfSelected = 0; PointDataIterator it; for (it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++) { if (it->Value().selected == true) { ++numberOfSelected; } } return numberOfSelected; } int mitk::PointSet::SearchSelectedPoint(int t) const { if ((unsigned int)t >= m_PointSetSeries.size()) { return -1; } PointDataIterator it; for (it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++) { if (it->Value().selected == true) { return it->Index(); } } return -1; } void mitk::PointSet::ExecuteOperation(Operation *operation) { int timeStep = -1; mitkCheckOperationTypeMacro(PointOperation, operation, pointOp); if (pointOp) { timeStep = this->GetTimeGeometry()->TimePointToTimeStep(pointOp->GetTimeInMS()); } if (timeStep < 0) { MITK_ERROR << "Time step (" << timeStep << ") outside of PointSet time bounds" << std::endl; return; } switch (operation->GetOperationType()) { case OpNOTHING: break; case OpINSERT: // inserts the point at the given position and selects it. { int position = pointOp->GetIndex(); PointType pt; pt.CastFrom(pointOp->GetPoint()); if (timeStep >= (int)this->GetTimeSteps()) this->Expand(timeStep + 1); // transfer from world to index coordinates mitk::BaseGeometry *geometry = this->GetGeometry(timeStep); if (geometry == nullptr) { MITK_INFO << "GetGeometry returned nullptr!\n"; return; } geometry->WorldToIndex(pt, pt); m_PointSetSeries[timeStep]->GetPoints()->InsertElement(position, pt); PointDataType pointData = { static_cast(pointOp->GetIndex()), pointOp->GetSelected(), pointOp->GetPointType()}; m_PointSetSeries[timeStep]->GetPointData()->InsertElement(position, pointData); this->Modified(); // boundingbox has to be computed m_CalculateBoundingBox = true; this->InvokeEvent(PointSetAddEvent()); this->OnPointSetChange(); } break; case OpMOVE: // moves the point given by index { PointType pt; pt.CastFrom(pointOp->GetPoint()); // transfer from world to index coordinates this->GetGeometry(timeStep)->WorldToIndex(pt, pt); // Copy new point into container m_PointSetSeries[timeStep]->SetPoint(pointOp->GetIndex(), pt); // Insert a default point data object to keep the containers in sync // (if no point data object exists yet) PointDataType pointData; if (!m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData)) { m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); } this->OnPointSetChange(); this->Modified(); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent(PointSetMoveEvent()); } break; case OpREMOVE: // removes the point at given by position { m_PointSetSeries[timeStep]->GetPoints()->DeleteIndex((unsigned)pointOp->GetIndex()); m_PointSetSeries[timeStep]->GetPointData()->DeleteIndex((unsigned)pointOp->GetIndex()); this->OnPointSetChange(); this->Modified(); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent(PointSetRemoveEvent()); } break; case OpSELECTPOINT: // select the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = true; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpDESELECTPOINT: // unselect the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = false; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpSETPOINTTYPE: { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.pointSpec = pointOp->GetPointType(); m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpMOVEPOINTUP: // swap content of point with ID pointOp->GetIndex() with the point preceding it in the // container // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that precedes this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); auto it = points.find(currentID); if (it == points.end()) // ID not found break; if (it == points.begin()) // we are at the first element, there is no previous element break; /* get and cache current point & pointdata and previous point & pointdata */ --it; PointIdentifier prevID = it->first; if (this->SwapPointContents(prevID, currentID, timeStep) == true) this->Modified(); } break; case OpMOVEPOINTDOWN: // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that succeeds this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); auto it = points.find(currentID); if (it == points.end()) // ID not found break; ++it; if (it == points.end()) // ID is already the last element, there is no succeeding element break; /* get and cache current point & pointdata and previous point & pointdata */ PointIdentifier nextID = it->first; if (this->SwapPointContents(nextID, currentID, timeStep) == true) this->Modified(); } break; default: itkWarningMacro("mitkPointSet could not understrand the operation. Please check!"); break; } // to tell the mappers, that the data is modified and has to be updated // only call modified if anything is done, so call in cases // this->Modified(); mitk::OperationEndEvent endevent(operation); ((const itk::Object *)this)->InvokeEvent(endevent); //*todo has to be done here, cause of update-pipeline not working yet // As discussed lately, don't mess with the rendering from inside data structures // mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PointSet::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } // // first make sure, that the associated time sliced geometry has // the same number of geometry 3d's as PointSets are present // TimeGeometry *timeGeometry = GetTimeGeometry(); if (timeGeometry->CountTimeSteps() != m_PointSetSeries.size()) { itkExceptionMacro(<< "timeGeometry->CountTimeSteps() != m_PointSetSeries.size() -- use Initialize(timeSteps) with " "correct number of timeSteps!"); } // This is needed to detect zero objects mitk::ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsNull(nullpoint); // // Iterate over the PointSets and update the Geometry // information of each of the items. // if (m_CalculateBoundingBox) { for (unsigned int i = 0; i < m_PointSetSeries.size(); ++i) { const DataType::BoundingBoxType *bb = m_PointSetSeries[i]->GetBoundingBox(); BoundingBox::BoundsArrayType itkBounds = bb->GetBounds(); if (m_PointSetSeries[i].IsNull() || (m_PointSetSeries[i]->GetNumberOfPoints() == 0) || (itkBounds == itkBoundsNull)) { itkBounds = itkBoundsNull; continue; } // Ensure minimal bounds of 1.0 in each dimension for (unsigned int j = 0; j < 3; ++j) { if (itkBounds[j * 2 + 1] - itkBounds[j * 2] < 1.0) { BoundingBox::CoordRepType center = (itkBounds[j * 2] + itkBounds[j * 2 + 1]) / 2.0; itkBounds[j * 2] = center - 0.5; itkBounds[j * 2 + 1] = center + 0.5; } } this->GetGeometry(i)->SetBounds(itkBounds); } m_CalculateBoundingBox = false; } this->GetTimeGeometry()->Update(); } void mitk::PointSet::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::PointSet::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::PointSet::VerifyRequestedRegion() { return true; } void mitk::PointSet::SetRequestedRegion(const DataObject *) { } void mitk::PointSet::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Number timesteps: " << m_PointSetSeries.size() << "\n"; unsigned int i = 0; for (auto it = m_PointSetSeries.begin(); it != m_PointSetSeries.end(); ++it) { os << indent << "Timestep " << i++ << ": \n"; MeshType::Pointer ps = *it; itk::Indent nextIndent = indent.GetNextIndent(); ps->Print(os, nextIndent); MeshType::PointsContainer *points = ps->GetPoints(); MeshType::PointDataContainer *datas = ps->GetPointData(); MeshType::PointDataContainer::Iterator dataIterator = datas->Begin(); for (MeshType::PointsContainer::Iterator pointIterator = points->Begin(); pointIterator != points->End(); ++pointIterator, ++dataIterator) { os << nextIndent << "Point " << pointIterator->Index() << ": ["; os << pointIterator->Value().GetElement(0); for (unsigned int i = 1; i < PointType::GetPointDimension(); ++i) { os << ", " << pointIterator->Value().GetElement(i); } os << "]"; os << ", selected: " << dataIterator->Value().selected << ", point spec: " << dataIterator->Value().pointSpec << "\n"; } } } bool mitk::PointSet::SwapPointContents(PointIdentifier id1, PointIdentifier id2, int timeStep) { /* search and cache contents */ PointType p1; if (m_PointSetSeries[timeStep]->GetPoint(id1, &p1) == false) return false; PointDataType data1; if (m_PointSetSeries[timeStep]->GetPointData(id1, &data1) == false) return false; PointType p2; if (m_PointSetSeries[timeStep]->GetPoint(id2, &p2) == false) return false; PointDataType data2; if (m_PointSetSeries[timeStep]->GetPointData(id2, &data2) == false) return false; /* now swap contents */ m_PointSetSeries[timeStep]->SetPoint(id1, p2); m_PointSetSeries[timeStep]->SetPointData(id1, data2); m_PointSetSeries[timeStep]->SetPoint(id2, p1); m_PointSetSeries[timeStep]->SetPointData(id2, data1); return true; } bool mitk::PointSet::PointDataType::operator==(const mitk::PointSet::PointDataType &other) const { return id == other.id && selected == other.selected && pointSpec == other.pointSpec; } bool mitk::Equal(const mitk::PointSet *leftHandSide, const mitk::PointSet *rightHandSide, mitk::ScalarType eps, bool verbose, bool checkGeometry) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, " "mitk::ScalarType eps, bool verbose ) does not work with nullptr pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose, checkGeometry); } bool mitk::Equal(const mitk::PointSet &leftHandSide, const mitk::PointSet &rightHandSide, mitk::ScalarType eps, bool verbose, bool checkGeometry) { bool result = true; // If comparing point sets from file, you must not compare the geometries, as they are not saved. In other cases, you // do need to check them. if (checkGeometry) { if (!mitk::Equal(*leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose)) { if (verbose) MITK_INFO << "[( PointSet )] Geometries differ."; result = false; } } if (leftHandSide.GetSize() != rightHandSide.GetSize()) { if (verbose) MITK_INFO << "[( PointSet )] Number of points differ."; result = false; } else { // if the size is equal, we compare the point values mitk::Point3D pointLeftHandSide; mitk::Point3D pointRightHandSide; int numberOfIncorrectPoints = 0; // Iterate over both pointsets in order to compare all points pair-wise mitk::PointSet::PointsConstIterator end = leftHandSide.End(); for (mitk::PointSet::PointsConstIterator pointSetIteratorLeft = leftHandSide.Begin(), pointSetIteratorRight = rightHandSide.Begin(); pointSetIteratorLeft != end; ++pointSetIteratorLeft, ++pointSetIteratorRight) // iterate simultaneously over both sets { pointLeftHandSide = pointSetIteratorLeft.Value(); pointRightHandSide = pointSetIteratorRight.Value(); if (!mitk::Equal(pointLeftHandSide, pointRightHandSide, eps, verbose)) { if (verbose) MITK_INFO << "[( PointSet )] Point values are different."; result = false; numberOfIncorrectPoints++; } } if ((numberOfIncorrectPoints > 0) && verbose) { MITK_INFO << numberOfIncorrectPoints << " of a total of " << leftHandSide.GetSize() << " points are different."; } } return result; } diff --git a/Modules/Core/src/DataManagement/mitkPropertyList.cpp b/Modules/Core/src/DataManagement/mitkPropertyList.cpp index d9ad1ac009..994d5af78c 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyList.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyList.cpp @@ -1,376 +1,376 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkPropertyList.h" #include "mitkNumericTypes.h" #include "mitkProperties.h" #include "mitkStringProperty.h" mitk::BaseProperty::ConstPointer mitk::PropertyList::GetConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) const { PropertyMap::const_iterator it; it = m_Properties.find(propertyKey); if (it != m_Properties.cend()) return it->second.GetPointer(); else return nullptr; }; std::vector mitk::PropertyList::GetPropertyKeys(const std::string &contextName, bool includeDefaultContext) const { std::vector propertyKeys; if (contextName.empty() || includeDefaultContext) { for (const auto &property : this->m_Properties) propertyKeys.push_back(property.first); } return propertyKeys; }; std::vector mitk::PropertyList::GetPropertyContextNames() const { return std::vector(); }; mitk::BaseProperty *mitk::PropertyList::GetProperty(const std::string &propertyKey) const { PropertyMap::const_iterator it; it = m_Properties.find(propertyKey); if (it != m_Properties.cend()) return it->second; else return nullptr; } mitk::BaseProperty * mitk::PropertyList::GetNonConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { return this->GetProperty(propertyKey); } void mitk::PropertyList::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (!property) return; // make sure that BaseProperty*, which may have just been created and never been // assigned to a SmartPointer, is registered/unregistered properly. If we do not // do that, it will a) not deleted in case it is identical to the old one or // b) possibly deleted when temporarily added to a smartpointer somewhere below. BaseProperty::Pointer tmpSmartPointerToProperty = property; auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { // yes // is the property contained in the list identical to the new one? if (it->second->operator==(*property)) { // yes? do nothing and return. return; } if (it->second->AssignProperty(*property)) { // The assignment was successfull this->Modified(); } else { MITK_ERROR << "In " __FILE__ ", l." << __LINE__ << ": Trying to set existing property " << it->first << " of type " << it->second->GetNameOfClass() << " to a property with different type " << property->GetNameOfClass() << "." << " Use ReplaceProperty() instead." << std::endl; } return; } // no? add it. m_Properties.insert(PropertyMap::value_type(propertyKey, property)); this->Modified(); } void mitk::PropertyList::ReplaceProperty(const std::string &propertyKey, BaseProperty *property) { if (!property) return; auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { it->second = nullptr; m_Properties.erase(it); } // no? add/replace it. m_Properties.insert(PropertyMap::value_type(propertyKey, property)); Modified(); } void mitk::PropertyList::RemoveProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { it->second = nullptr; m_Properties.erase(it); Modified(); } } mitk::PropertyList::PropertyList() { } mitk::PropertyList::PropertyList(const mitk::PropertyList &other) : itk::Object() { for (auto i = other.m_Properties.cbegin(); i != other.m_Properties.cend(); ++i) { m_Properties.insert(std::make_pair(i->first, i->second->Clone())); } } mitk::PropertyList::~PropertyList() { Clear(); } /** * Consider the list as changed when any of the properties has changed recently. */ -unsigned long mitk::PropertyList::GetMTime() const +itk::ModifiedTimeType mitk::PropertyList::GetMTime() const { for (auto it = m_Properties.cbegin(); it != m_Properties.cend(); ++it) { if (it->second.IsNull()) { itkWarningMacro(<< "Property '" << it->first << "' contains nothing (nullptr)."); continue; } if (Superclass::GetMTime() < it->second->GetMTime()) { Modified(); break; } } return Superclass::GetMTime(); } bool mitk::PropertyList::DeleteProperty(const std::string &propertyKey) { auto it = m_Properties.find(propertyKey); if (it != m_Properties.end()) { it->second = nullptr; m_Properties.erase(it); Modified(); return true; } return false; } void mitk::PropertyList::Clear() { auto it = m_Properties.begin(), end = m_Properties.end(); while (it != end) { it->second = nullptr; ++it; } m_Properties.clear(); } itk::LightObject::Pointer mitk::PropertyList::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } void mitk::PropertyList::ConcatenatePropertyList(PropertyList *pList, bool replace) { if (pList) { const PropertyMap *propertyMap = pList->GetMap(); for (auto iter = propertyMap->cbegin(); // m_PropertyList is created in the constructor, so we don't check it here iter != propertyMap->cend(); ++iter) { const std::string key = iter->first; BaseProperty *value = iter->second; if (replace) { ReplaceProperty(key.c_str(), value); } else { SetProperty(key.c_str(), value); } } } } bool mitk::PropertyList::GetBoolProperty(const char *propertyKey, bool &boolValue) const { BoolProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { boolValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, boolValue); } bool mitk::PropertyList::GetIntProperty(const char *propertyKey, int &intValue) const { IntProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { intValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, intValue); } bool mitk::PropertyList::GetFloatProperty(const char *propertyKey, float &floatValue) const { FloatProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { floatValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue(propertyKey, floatValue); } bool mitk::PropertyList::GetStringProperty(const char *propertyKey, std::string &stringValue) const { StringProperty *sp = dynamic_cast(GetProperty(propertyKey)); if (sp != nullptr) { stringValue = sp->GetValue(); return true; } return false; } void mitk::PropertyList::SetIntProperty(const char *propertyKey, int intValue) { SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::PropertyList::SetBoolProperty(const char *propertyKey, bool boolValue) { SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::PropertyList::SetFloatProperty(const char *propertyKey, float floatValue) { SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::PropertyList::SetStringProperty(const char *propertyKey, const char *stringValue) { SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::PropertyList::Set(const char *propertyKey, bool boolValue) { this->SetBoolProperty(propertyKey, boolValue); } void mitk::PropertyList::Set(const char *propertyKey, int intValue) { this->SetIntProperty(propertyKey, intValue); } void mitk::PropertyList::Set(const char *propertyKey, float floatValue) { this->SetFloatProperty(propertyKey, floatValue); } void mitk::PropertyList::Set(const char *propertyKey, double doubleValue) { this->SetDoubleProperty(propertyKey, doubleValue); } void mitk::PropertyList::Set(const char *propertyKey, const char *stringValue) { this->SetStringProperty(propertyKey, stringValue); } void mitk::PropertyList::Set(const char *propertyKey, const std::string &stringValue) { this->SetStringProperty(propertyKey, stringValue.c_str()); } bool mitk::PropertyList::Get(const char *propertyKey, bool &boolValue) const { return this->GetBoolProperty(propertyKey, boolValue); } bool mitk::PropertyList::Get(const char *propertyKey, int &intValue) const { return this->GetIntProperty(propertyKey, intValue); } bool mitk::PropertyList::Get(const char *propertyKey, float &floatValue) const { return this->GetFloatProperty(propertyKey, floatValue); } bool mitk::PropertyList::Get(const char *propertyKey, double &doubleValue) const { return this->GetDoubleProperty(propertyKey, doubleValue); } bool mitk::PropertyList::Get(const char *propertyKey, std::string &stringValue) const { return this->GetStringProperty(propertyKey, stringValue); } bool mitk::PropertyList::GetDoubleProperty(const char *propertyKey, double &doubleValue) const { DoubleProperty *gp = dynamic_cast(GetProperty(propertyKey)); if (gp != nullptr) { doubleValue = gp->GetValue(); return true; } return false; } void mitk::PropertyList::SetDoubleProperty(const char *propertyKey, double doubleValue) { SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); } diff --git a/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp index 6b69557d23..4eedfcceeb 100644 --- a/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp +++ b/Modules/Core/src/DataManagement/mitkStandaloneDataStorage.cpp @@ -1,258 +1,256 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkStandaloneDataStorage.h" -#include "itkMutexLockHolder.h" -#include "itkSimpleFastMutexLock.h" #include "mitkDataNode.h" #include "mitkGroupTagProperty.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" mitk::StandaloneDataStorage::StandaloneDataStorage() : mitk::DataStorage() { } mitk::StandaloneDataStorage::~StandaloneDataStorage() { for (auto it = m_SourceNodes.begin(); it != m_SourceNodes.end(); ++it) { this->RemoveListeners(it->first); } } bool mitk::StandaloneDataStorage::IsInitialized() const { return true; } void mitk::StandaloneDataStorage::Add(mitk::DataNode *node, const mitk::DataStorage::SetOfObjects *parents) { { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); /* check if node is in its own list of sources */ if ((parents != nullptr) && (std::find(parents->begin(), parents->end(), node) != parents->end())) throw std::invalid_argument("Node is it's own parent"); /* check if node already exists in StandaloneDataStorage */ if (m_SourceNodes.find(node) != m_SourceNodes.end()) throw std::invalid_argument("Node is already in DataStorage"); /* create parent list if it does not exist */ mitk::DataStorage::SetOfObjects::ConstPointer sp; if (parents != nullptr) sp = parents; else sp = mitk::DataStorage::SetOfObjects::New(); /* Store node and parent list in sources adjacency list */ m_SourceNodes.insert(std::make_pair(node, sp)); /* Store node and an empty children list in derivations adjacency list */ mitk::DataStorage::SetOfObjects::Pointer childrenPointer = mitk::DataStorage::SetOfObjects::New(); mitk::DataStorage::SetOfObjects::ConstPointer children = childrenPointer.GetPointer(); m_DerivedNodes.insert(std::make_pair(node, children)); /* create entry in derivations adjacency list for each parent of the new node */ for (SetOfObjects::ConstIterator it = sp->Begin(); it != sp->End(); it++) { mitk::DataNode::ConstPointer parent = it.Value().GetPointer(); mitk::DataStorage::SetOfObjects::ConstPointer derivedObjects = m_DerivedNodes[parent]; // get or create pointer to list of derived objects for that parent node if (derivedObjects.IsNull()) m_DerivedNodes[parent] = mitk::DataStorage::SetOfObjects::New(); // Create a set of Objects, if it does not already exist auto *deob = const_cast( m_DerivedNodes[parent].GetPointer()); // temporarily get rid of const pointer to insert new element deob->InsertElement(deob->Size(), node); // node is derived from parent. Insert it into the parents list of derived objects } // register for ITK changed events this->AddListeners(node); } /* Notify observers */ EmitAddNodeEvent(node); } void mitk::StandaloneDataStorage::Remove(const mitk::DataNode *node) { if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); if (node == nullptr) return; // remove ITK modified event listener this->RemoveListeners(node); // muellerm, 22.9.10: add additional reference count to ensure // that the node is not deleted when removed from the relation map // while m_Mutex is locked. This would cause the an itk::DeleteEvent // is thrown and a deadlock will occur when event receivers // access the DataStorage again in their event processing function // mitk::DataNode::ConstPointer nodeGuard(node); /* Notify observers of imminent node removal */ EmitRemoveNodeEvent(node); { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); /* remove node from both relation adjacency lists */ this->RemoveFromRelation(node, m_SourceNodes); this->RemoveFromRelation(node, m_DerivedNodes); } } bool mitk::StandaloneDataStorage::Exists(const mitk::DataNode *node) const { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); return (m_SourceNodes.find(node) != m_SourceNodes.end()); } void mitk::StandaloneDataStorage::RemoveFromRelation(const mitk::DataNode *node, AdjacencyList &relation) { for (auto mapIter = relation.cbegin(); mapIter != relation.cend(); ++mapIter) // for each node in the relation if (mapIter->second.IsNotNull()) // if node has a relation list { SetOfObjects::Pointer s = const_cast(mapIter->second.GetPointer()); // search for node to be deleted in the relation list auto relationListIter = std::find( s->begin(), s->end(), node); // this assumes, that the relation list does not contain duplicates (which should be safe to assume) if (relationListIter != s->end()) // if node to be deleted is in relation list s->erase(relationListIter); // remove it from parentlist } /* now remove node from the relation */ AdjacencyList::iterator adIt; adIt = relation.find(node); if (adIt != relation.end()) relation.erase(adIt); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetAll() const { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); if (!IsInitialized()) throw std::logic_error("DataStorage not initialized"); mitk::DataStorage::SetOfObjects::Pointer resultset = mitk::DataStorage::SetOfObjects::New(); /* Fill resultset with all objects that are managed by the StandaloneDataStorage object */ unsigned int index = 0; for (auto it = m_SourceNodes.cbegin(); it != m_SourceNodes.cend(); ++it) if (it->first.IsNull()) continue; else resultset->InsertElement(index++, const_cast(it->first.GetPointer())); return SetOfObjects::ConstPointer(resultset); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetRelations( const mitk::DataNode *node, const AdjacencyList &relation, const NodePredicateBase *condition, bool onlyDirectlyRelated) const { if (node == nullptr) throw std::invalid_argument("invalid node"); /* Either read direct relations directly from adjacency list */ if (onlyDirectlyRelated) { auto it = relation.find(node); // get parents of current node if ((it == relation.cend()) || (it->second.IsNull())) // node not found in list or no set of parents return SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return an empty set else return this->FilterSetOfObjects(it->second, condition); } /* Or traverse adjacency list to collect all related nodes */ std::vector resultset; std::vector openlist; /* Initialize openlist with node. this will add node to resultset, but that is necessary to detect circular relations that would lead to endless recursion */ openlist.push_back(node); while (openlist.size() > 0) { mitk::DataNode::ConstPointer current = openlist.back(); // get element that needs to be processed openlist.pop_back(); // remove last element, because it gets processed now resultset.push_back(current); // add current element to resultset auto it = relation.find(current); // get parents of current node if ((it == relation.cend()) // if node not found in list || (it->second.IsNull()) // or no set of parents available || (it->second->Size() == 0)) // or empty set of parents continue; // then continue with next node in open list else for (SetOfObjects::ConstIterator parentIt = it->second->Begin(); parentIt != it->second->End(); ++parentIt) // for each parent of current node { mitk::DataNode::ConstPointer p = parentIt.Value().GetPointer(); if (!(std::find(resultset.cbegin(), resultset.cend(), p) != resultset.end()) // if it is not already in resultset && !(std::find(openlist.cbegin(), openlist.cend(), p) != openlist.cend())) // and not already in openlist openlist.push_back(p); // then add it to openlist, so that it can be processed } } /* now finally copy the results to a proper SetOfObjects variable exluding the initial node and checking the condition * if any is given */ mitk::DataStorage::SetOfObjects::Pointer realResultset = mitk::DataStorage::SetOfObjects::New(); if (condition != nullptr) { for (auto resultIt = resultset.cbegin(); resultIt != resultset.cend(); ++resultIt) if ((*resultIt != node) && (condition->CheckNode(*resultIt) == true)) realResultset->InsertElement(realResultset->Size(), mitk::DataNode::Pointer(const_cast((*resultIt).GetPointer()))); } else { for (auto resultIt = resultset.cbegin(); resultIt != resultset.cend(); ++resultIt) if (*resultIt != node) realResultset->InsertElement(realResultset->Size(), mitk::DataNode::Pointer(const_cast((*resultIt).GetPointer()))); } return SetOfObjects::ConstPointer(realResultset); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetSources( const mitk::DataNode *node, const NodePredicateBase *condition, bool onlyDirectSources) const { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); return this->GetRelations(node, m_SourceNodes, condition, onlyDirectSources); } mitk::DataStorage::SetOfObjects::ConstPointer mitk::StandaloneDataStorage::GetDerivations( const mitk::DataNode *node, const NodePredicateBase *condition, bool onlyDirectDerivations) const { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); return this->GetRelations(node, m_DerivedNodes, condition, onlyDirectDerivations); } void mitk::StandaloneDataStorage::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << "StandaloneDataStorage:\n"; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Core/src/IO/mitkAbstractFileReader.cpp b/Modules/Core/src/IO/mitkAbstractFileReader.cpp index 4ac0024740..3085cb6efd 100644 --- a/Modules/Core/src/IO/mitkAbstractFileReader.cpp +++ b/Modules/Core/src/IO/mitkAbstractFileReader.cpp @@ -1,334 +1,334 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 = this->DoRead(); const auto options = this->GetOptions(); for (auto& data : result) { 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(IOUtil::Local8BitToUtf8(this->GetInputLocation()))); + data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_INPUTLOCATION()), StringProperty::New(Utf8Util::Local8BitToUtf8(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)) + if (itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(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::string ext = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(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()) { - auto path = itksys::SystemTools::GetFilenamePath(filePath); - path = IOUtil::Local8BitToUtf8(path); + auto path = itksys::SystemTools::GetFilenamePath(Utf8Util::Local8BitToUtf8(filePath)); node->SetProperty(StringProperty::PATH, mitk::StringProperty::New(path)); } // 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; auto name = this->GetRegisteredMimeType().GetFilenameWithoutExtension(filePath); - name = IOUtil::Local8BitToUtf8(name); + name = Utf8Util::Local8BitToUtf8(name); nameProp = mitk::StringProperty::New(name); 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/mitkAbstractFileWriter.cpp b/Modules/Core/src/IO/mitkAbstractFileWriter.cpp index 30220b75d7..072460a076 100644 --- a/Modules/Core/src/IO/mitkAbstractFileWriter.cpp +++ b/Modules/Core/src/IO/mitkAbstractFileWriter.cpp @@ -1,288 +1,289 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 { struct AbstractFileWriter::LocalFile::Impl { Impl(const std::string &location, std::ostream *os) : m_Location(location), m_Stream(os) {} std::string m_Location; std::string m_TmpFileName; std::ostream *m_Stream; }; AbstractFileWriter::LocalFile::LocalFile(IFileWriter *writer) : d(new Impl(writer->GetOutputLocation(), writer->GetOutputStream())) { } AbstractFileWriter::LocalFile::~LocalFile() { if (d->m_Stream && !d->m_TmpFileName.empty()) { std::ifstream ifs(d->m_TmpFileName.c_str(), std::ios_base::binary); *d->m_Stream << ifs.rdbuf(); d->m_Stream->flush(); ifs.close(); std::remove(d->m_TmpFileName.c_str()); } } std::string AbstractFileWriter::LocalFile::GetFileName() { if (d->m_Stream == nullptr) { return d->m_Location; } else if (d->m_TmpFileName.empty()) { - std::string ext = itksys::SystemTools::GetFilenameExtension(d->m_Location); + std::string ext = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(d->m_Location))); d->m_TmpFileName = IOUtil::CreateTemporaryFile("XXXXXX" + ext); } return d->m_TmpFileName; } AbstractFileWriter::OutputStream::OutputStream(IFileWriter *writer, std::ios_base::openmode mode) : std::ostream(nullptr), m_Stream(nullptr) { std::ostream *stream = writer->GetOutputStream(); if (stream) { this->init(stream->rdbuf()); } else { m_Stream = new std::ofstream(writer->GetOutputLocation().c_str(), mode); this->init(m_Stream->rdbuf()); } } AbstractFileWriter::OutputStream::~OutputStream() { delete m_Stream; } class AbstractFileWriter::Impl : public FileReaderWriterBase { public: Impl() : FileReaderWriterBase(), m_BaseData(nullptr), m_Stream(nullptr), m_PrototypeFactory(nullptr) {} Impl(const Impl &other) : FileReaderWriterBase(other), m_BaseDataType(other.m_BaseDataType), m_BaseData(nullptr), m_Stream(nullptr), m_PrototypeFactory(nullptr) { } std::string m_BaseDataType; const BaseData *m_BaseData; std::string m_Location; std::ostream *m_Stream; us::PrototypeServiceFactory *m_PrototypeFactory; us::ServiceRegistration m_Reg; }; void AbstractFileWriter::SetInput(const BaseData *data) { d->m_BaseData = data; } const BaseData *AbstractFileWriter::GetInput() const { return d->m_BaseData; } void AbstractFileWriter::SetOutputLocation(const std::string &location) { d->m_Location = location; d->m_Stream = nullptr; } std::string AbstractFileWriter::GetOutputLocation() const { return d->m_Location; } void AbstractFileWriter::SetOutputStream(const std::string &location, std::ostream *os) { d->m_Location = location; d->m_Stream = os; } std::ostream *AbstractFileWriter::GetOutputStream() const { return d->m_Stream; } AbstractFileWriter::~AbstractFileWriter() { UnregisterService(); delete d->m_PrototypeFactory; } AbstractFileWriter::AbstractFileWriter(const AbstractFileWriter &other) : IFileWriter(), d(new Impl(*other.d.get())) { } AbstractFileWriter::AbstractFileWriter(const std::string &baseDataType) : d(new Impl) { d->m_BaseDataType = baseDataType; } AbstractFileWriter::AbstractFileWriter(const std::string &baseDataType, const CustomMimeType &mimeType, const std::string &description) : d(new Impl) { d->m_BaseDataType = baseDataType; d->SetMimeType(mimeType); d->SetDescription(description); } ////////////////////// Writing ///////////////////////// IFileWriter::ConfidenceLevel AbstractFileWriter::GetConfidenceLevel() const { if (d->m_BaseData == nullptr) return Unsupported; std::vector classHierarchy = d->m_BaseData->GetClassHierarchy(); if (std::find(classHierarchy.begin(), classHierarchy.end(), d->m_BaseDataType) == classHierarchy.end()) { return Unsupported; } return Supported; } MimeType AbstractFileWriter::GetRegisteredMimeType() const { return d->GetRegisteredMimeType(); } //////////// µS Registration & Properties ////////////// us::ServiceRegistration AbstractFileWriter::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 writer due to empty MIME type."; return us::ServiceRegistration(); } struct PrototypeFactory : public us::PrototypeServiceFactory { AbstractFileWriter *const m_Prototype; PrototypeFactory(AbstractFileWriter *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 AbstractFileWriter::UnregisterService() { try { d->m_Reg.Unregister(); } catch (const std::exception &) { } } us::ServiceProperties AbstractFileWriter::GetServiceProperties() const { us::ServiceProperties result; result[IFileWriter::PROP_DESCRIPTION()] = this->GetDescription(); result[IFileWriter::PROP_MIMETYPE()] = this->GetMimeType()->GetName(); result[IFileWriter::PROP_BASEDATA_TYPE()] = d->m_BaseDataType; result[us::ServiceConstants::SERVICE_RANKING()] = this->GetRanking(); // for (IFileWriter::OptionList::const_iterator it = d->m_Options.begin(); it != d->m_Options.end(); ++it) // { // result[it->first] = std::string("true"); // } return result; } const CustomMimeType *AbstractFileWriter::GetMimeType() const { return d->GetMimeType(); } void AbstractFileWriter::SetMimeTypePrefix(const std::string &prefix) { d->SetMimeTypePrefix(prefix); } std::string AbstractFileWriter::GetMimeTypePrefix() const { return d->GetMimeTypePrefix(); } us::ServiceRegistration AbstractFileWriter::RegisterMimeType(us::ModuleContext *context) { return d->RegisterMimeType(context); } void AbstractFileWriter::SetMimeType(const CustomMimeType &mimeType) { d->SetMimeType(mimeType); } void AbstractFileWriter::SetRanking(int ranking) { d->SetRanking(ranking); } //////////////////////// Options /////////////////////// void AbstractFileWriter::SetDefaultOptions(const IFileWriter::Options &defaultOptions) { d->SetDefaultOptions(defaultOptions); } IFileWriter::Options AbstractFileWriter::GetDefaultOptions() const { return d->GetDefaultOptions(); } IFileWriter::Options AbstractFileWriter::GetOptions() const { return d->GetOptions(); } us::Any AbstractFileWriter::GetOption(const std::string &name) const { return d->GetOption(name); } void AbstractFileWriter::SetOption(const std::string &name, const us::Any &value) { d->SetOption(name, value); } void AbstractFileWriter::SetOptions(const Options &options) { d->SetOptions(options); } ////////////////// MISC ////////////////// void AbstractFileWriter::AddProgressCallback(const ProgressCallback &callback) { d->AddProgressCallback(callback); } void AbstractFileWriter::RemoveProgressCallback(const ProgressCallback &callback) { d->RemoveProgressCallback(callback); } ////////////////// µS related Getters ////////////////// int AbstractFileWriter::GetRanking() const { return d->GetRanking(); } void AbstractFileWriter::SetBaseDataType(const std::string &baseDataType) { d->m_BaseDataType = baseDataType; } std::string AbstractFileWriter::GetDescription() const { return d->GetDescription(); } std::string AbstractFileWriter::GetBaseDataType() const { return d->m_BaseDataType; } void AbstractFileWriter::ValidateOutputLocation() const { if (this->GetOutputStream() == nullptr) { // check if a file name is set and if we can write to it const std::string fileName = this->GetOutputLocation(); if (fileName.empty()) { mitkThrow() << "No output location or stream specified"; } } } void AbstractFileWriter::SetDescription(const std::string &description) { d->SetDescription(description); } } diff --git a/Modules/Core/src/IO/mitkCustomMimeType.cpp b/Modules/Core/src/IO/mitkCustomMimeType.cpp index 30b54ef962..77b3aa0507 100644 --- a/Modules/Core/src/IO/mitkCustomMimeType.cpp +++ b/Modules/Core/src/IO/mitkCustomMimeType.cpp @@ -1,161 +1,163 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCustomMimeType.h" #include "mitkMimeType.h" +#include + #include #include namespace mitk { class FindCaseInsensitive { public: FindCaseInsensitive(std::string value) { lcValue.resize(value.size()); std::transform(value.begin(), value.end(), lcValue.begin(), ::tolower); } bool operator()(std::string elem) { std::transform(elem.begin(), elem.end(), elem.begin(), ::tolower); return elem == lcValue; } private: std::string lcValue; }; struct CustomMimeType::Impl { std::string m_Name; std::string m_Category; std::vector m_Extensions; std::string m_Comment; }; CustomMimeType::~CustomMimeType() { delete d; } CustomMimeType::CustomMimeType() : d(new Impl) {} CustomMimeType::CustomMimeType(const std::string &name) : d(new Impl) { d->m_Name = name; } CustomMimeType::CustomMimeType(const CustomMimeType &other) : d(new Impl(*other.d)) {} CustomMimeType::CustomMimeType(const MimeType &other) : d(new Impl) { d->m_Name = other.GetName(); d->m_Category = other.GetCategory(); d->m_Extensions = other.GetExtensions(); d->m_Comment = other.GetComment(); } CustomMimeType &CustomMimeType::operator=(const CustomMimeType &other) { CustomMimeType tmp(other); Swap(tmp); return *this; } CustomMimeType &CustomMimeType::operator=(const MimeType &other) { CustomMimeType tmp(other); Swap(tmp); return *this; } std::string CustomMimeType::GetName() const { return d->m_Name; } std::string CustomMimeType::GetCategory() const { return d->m_Category; } std::vector CustomMimeType::GetExtensions() const { return d->m_Extensions; } std::string CustomMimeType::GetComment() const { if (!d->m_Comment.empty()) return d->m_Comment; if (!d->m_Extensions.empty()) { return d->m_Extensions.front() + " File"; } return "Unknown"; } bool CustomMimeType::AppliesTo(const std::string &path) const { return MatchesExtension(path); } bool CustomMimeType::MatchesExtension(const std::string &path) const { std::string extension, filename; return ParsePathForExtension(path, extension, filename); } std::string CustomMimeType::GetExtension(const std::string &path) const { std::string extension, filename; ParsePathForExtension(path, extension, filename); return extension; } std::string CustomMimeType::GetFilenameWithoutExtension(const std::string &path) const { std::string extension, filename; ParsePathForExtension(path, extension, filename); return filename; } bool CustomMimeType::ParsePathForExtension(const std::string &path, std::string &extension, std::string &filename) const { for (std::vector::const_iterator iter = d->m_Extensions.begin(), iterEnd = d->m_Extensions.end(); iter != iterEnd; ++iter) { if (!iter->empty() && path.size() >= iter->size()) { FindCaseInsensitive cmp(*iter); if (cmp(path.substr(path.size() - iter->size()))) { extension = "." + *iter; - std::string filenameWithExtension = itksys::SystemTools::GetFilenameName(path); + std::string filenameWithExtension = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameName(Utf8Util::Local8BitToUtf8(path))); filename = filenameWithExtension.substr(0, filenameWithExtension.size() - extension.size()); return true; } } } return false; } void CustomMimeType::SetName(const std::string &name) { d->m_Name = name; } void CustomMimeType::SetCategory(const std::string &category) { d->m_Category = category; } void CustomMimeType::SetExtension(const std::string &extension) { d->m_Extensions.clear(); d->m_Extensions.push_back(extension); } void CustomMimeType::AddExtension(const std::string &extension) { if (std::find_if(d->m_Extensions.begin(), d->m_Extensions.end(), FindCaseInsensitive(extension)) == d->m_Extensions.end()) { d->m_Extensions.push_back(extension); } } void CustomMimeType::SetComment(const std::string &comment) { d->m_Comment = comment; } void CustomMimeType::Swap(CustomMimeType &r) { Impl *d1 = d; d = r.d; r.d = d1; } CustomMimeType *CustomMimeType::Clone() const { return new CustomMimeType(*this); } void swap(CustomMimeType &l, CustomMimeType &r) { l.Swap(r); } } diff --git a/Modules/Core/src/IO/mitkFileReaderSelector.cpp b/Modules/Core/src/IO/mitkFileReaderSelector.cpp index 5f7e2fb579..e4ef79a8a5 100644 --- a/Modules/Core/src/IO/mitkFileReaderSelector.cpp +++ b/Modules/Core/src/IO/mitkFileReaderSelector.cpp @@ -1,222 +1,223 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkFileReaderSelector.h" #include #include #include +#include #include #include #include #include namespace mitk { struct FileReaderSelector::Item::Impl : us::SharedData { Impl() : m_FileReader(nullptr), m_ConfidenceLevel(IFileReader::Unsupported), m_Id(-1) {} us::ServiceReference m_FileReaderRef; IFileReader *m_FileReader; IFileReader::ConfidenceLevel m_ConfidenceLevel; MimeType m_MimeType; long m_Id; }; struct FileReaderSelector::Impl : us::SharedData { Impl() : m_BestId(-1), m_SelectedId(m_BestId) {} Impl(const Impl &other) : us::SharedData(other), m_BestId(-1), m_SelectedId(m_BestId) {} FileReaderRegistry m_ReaderRegistry; std::map m_Items; std::vector m_MimeTypes; long m_BestId; long m_SelectedId; }; FileReaderSelector::FileReaderSelector(const FileReaderSelector &other) : m_Data(other.m_Data) {} FileReaderSelector::FileReaderSelector(const std::string &path) : m_Data(new Impl) { - if (!itksys::SystemTools::FileExists(path.c_str())) + if (!itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(path).c_str())) { return; } mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); // Get all mime types and associated readers for the given file path m_Data->m_MimeTypes = mimeTypeProvider->GetMimeTypesForFile(path); if (m_Data->m_MimeTypes.empty()) return; for (std::vector::const_iterator mimeTypeIter = m_Data->m_MimeTypes.begin(), mimeTypeIterEnd = m_Data->m_MimeTypes.end(); mimeTypeIter != mimeTypeIterEnd; ++mimeTypeIter) { std::vector refs = m_Data->m_ReaderRegistry.GetReferences(*mimeTypeIter); for (std::vector::const_iterator readerIter = refs.begin(), iterEnd = refs.end(); readerIter != iterEnd; ++readerIter) { IFileReader *reader = m_Data->m_ReaderRegistry.GetReader(*readerIter); if (reader == nullptr) continue; try { reader->SetInput(path); IFileReader::ConfidenceLevel confidenceLevel = reader->GetConfidenceLevel(); if (confidenceLevel == IFileReader::Unsupported) { continue; } Item item; item.d->m_FileReaderRef = *readerIter; item.d->m_FileReader = reader; item.d->m_ConfidenceLevel = confidenceLevel; item.d->m_MimeType = *mimeTypeIter; item.d->m_Id = us::any_cast(readerIter->GetProperty(us::ServiceConstants::SERVICE_ID())); m_Data->m_Items.insert(std::make_pair(item.d->m_Id, item)); // m_Data->m_MimeTypes.insert(mimeType); } catch (const us::BadAnyCastException &e) { MITK_WARN << "Unexpected: " << e.what(); } catch (const std::exception &e) { // Log the error but continue MITK_WARN << "IFileWriter::GetConfidenceLevel exception: " << e.what(); } } } // get the "best" reader if (!m_Data->m_Items.empty()) { std::set sortedItems; for (std::map::const_iterator iter = m_Data->m_Items.begin(), iterEnd = m_Data->m_Items.end(); iter != iterEnd; ++iter) { sortedItems.insert(iter->second); } m_Data->m_BestId = sortedItems.rbegin()->GetServiceId(); m_Data->m_SelectedId = m_Data->m_BestId; } } FileReaderSelector::~FileReaderSelector() {} FileReaderSelector &FileReaderSelector::operator=(const FileReaderSelector &other) { m_Data = other.m_Data; return *this; } bool FileReaderSelector::IsEmpty() const { return m_Data->m_Items.empty(); } std::vector FileReaderSelector::GetMimeTypes() const { return m_Data->m_MimeTypes; } std::vector FileReaderSelector::Get() const { std::vector result; for (std::map::const_iterator iter = m_Data->m_Items.begin(), iterEnd = m_Data->m_Items.end(); iter != iterEnd; ++iter) { result.push_back(iter->second); } std::sort(result.begin(), result.end()); return result; } FileReaderSelector::Item FileReaderSelector::Get(long id) const { std::map::const_iterator iter = m_Data->m_Items.find(id); if (iter != m_Data->m_Items.end()) { return iter->second; } return Item(); } FileReaderSelector::Item FileReaderSelector::GetDefault() const { return Get(m_Data->m_BestId); } long FileReaderSelector::GetDefaultId() const { return m_Data->m_BestId; } FileReaderSelector::Item FileReaderSelector::GetSelected() const { return Get(m_Data->m_SelectedId); } long FileReaderSelector::GetSelectedId() const { return m_Data->m_SelectedId; } bool FileReaderSelector::Select(const FileReaderSelector::Item &item) { return Select(item.d->m_Id); } bool FileReaderSelector::Select(long id) { if (id > -1) { if (m_Data->m_Items.find(id) == m_Data->m_Items.end()) { return false; } m_Data->m_SelectedId = id; return true; } return false; } void FileReaderSelector::Swap(FileReaderSelector &fws) { m_Data.Swap(fws.m_Data); } FileReaderSelector::Item::Item(const FileReaderSelector::Item &other) : d(other.d) {} FileReaderSelector::Item::~Item() {} FileReaderSelector::Item &FileReaderSelector::Item::operator=(const FileReaderSelector::Item &other) { d = other.d; return *this; } IFileReader *FileReaderSelector::Item::GetReader() const { return d->m_FileReader; } std::string FileReaderSelector::Item::GetDescription() const { us::Any descr = d->m_FileReaderRef.GetProperty(IFileReader::PROP_DESCRIPTION()); if (descr.Empty()) return std::string(); return descr.ToString(); } IFileReader::ConfidenceLevel FileReaderSelector::Item::GetConfidenceLevel() const { return d->m_ConfidenceLevel; } MimeType FileReaderSelector::Item::GetMimeType() const { return d->m_MimeType; } us::ServiceReference FileReaderSelector::Item::GetReference() const { return d->m_FileReaderRef; } long FileReaderSelector::Item::GetServiceId() const { return d->m_Id; } bool FileReaderSelector::Item::operator<(const FileReaderSelector::Item &other) const { // sort by confidence level first (ascending) if (d->m_ConfidenceLevel == other.d->m_ConfidenceLevel) { // sort by mime-type ranking if (d->m_MimeType < other.d->m_MimeType) { return true; } else if (other.d->m_MimeType < d->m_MimeType) { return false; } else { // sort by file writer service ranking return d->m_FileReaderRef < other.d->m_FileReaderRef; } } return d->m_ConfidenceLevel < other.d->m_ConfidenceLevel; } FileReaderSelector::Item::Item() : d(new Impl()) {} void swap(FileReaderSelector &frs1, FileReaderSelector &frs2) { frs1.Swap(frs2); } } diff --git a/Modules/Core/src/IO/mitkFileWriterSelector.cpp b/Modules/Core/src/IO/mitkFileWriterSelector.cpp index dbc14e6be4..116d6a2596 100644 --- a/Modules/Core/src/IO/mitkFileWriterSelector.cpp +++ b/Modules/Core/src/IO/mitkFileWriterSelector.cpp @@ -1,290 +1,291 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkFileWriterSelector.h" #include #include #include #include +#include #include #include #include #include #include #include #include namespace mitk { struct FileWriterSelector::Item::Impl : us::SharedData { Impl() : m_FileWriter(nullptr), m_ConfidenceLevel(IFileWriter::Unsupported), m_BaseDataIndex(0), m_Id(-1) {} us::ServiceReference m_FileWriterRef; IFileWriter *m_FileWriter; IFileWriter::ConfidenceLevel m_ConfidenceLevel; std::size_t m_BaseDataIndex; MimeType m_MimeType; long m_Id; }; struct FileWriterSelector::Impl : us::SharedData { Impl() : m_BestId(-1), m_SelectedId(m_BestId) {} Impl(const Impl &other) : us::SharedData(other), m_BestId(-1), m_SelectedId(m_BestId) {} FileWriterRegistry m_WriterRegistry; std::map m_Items; std::set m_MimeTypes; long m_BestId; long m_SelectedId; }; FileWriterSelector::FileWriterSelector(const FileWriterSelector &other) : m_Data(other.m_Data) {} FileWriterSelector::FileWriterSelector(const BaseData *baseData, const std::string &mimeType, const std::string &path) : m_Data(new Impl) { mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); std::vector refs; std::string destMimeType = mimeType; if (destMimeType.empty() && !path.empty()) { // try to derive a mime-type from the file std::vector mimeTypes = mimeTypeProvider->GetMimeTypesForFile(path); if (!mimeTypes.empty()) { for (unsigned int index = 0; index < mimeTypes.size(); index++) { std::vector tempRefs = m_Data->m_WriterRegistry.GetReferences(baseData, mimeTypes.at(index).GetName()); for (unsigned int innerIndex = 0; innerIndex < tempRefs.size(); innerIndex++) { refs.push_back(tempRefs.at(innerIndex)); } } } - else if (!itksys::SystemTools::GetFilenameExtension(path).empty()) + else if (!itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(path)).empty()) { // If there are no suitable mime-type for the file AND an extension // was supplied, we stop here. return; } else { refs = m_Data->m_WriterRegistry.GetReferences(baseData, destMimeType); } } else { refs = m_Data->m_WriterRegistry.GetReferences(baseData, destMimeType); } std::vector classHierarchy = baseData->GetClassHierarchy(); // Get all writers and their mime types for the given base data type Item bestItem; for (std::vector::const_iterator iter = refs.begin(), iterEnd = refs.end(); iter != iterEnd; ++iter) { std::string mimeTypeName = iter->GetProperty(IFileWriter::PROP_MIMETYPE()).ToString(); if (!mimeTypeName.empty()) { MimeType mimeType = mimeTypeProvider->GetMimeTypeForName(mimeTypeName); if (mimeType.IsValid()) { // There is a registered mime-type for this writer. Now get the confidence level // of this writer for writing the given base data object. IFileWriter *writer = m_Data->m_WriterRegistry.GetWriter(*iter); if (writer == nullptr) continue; try { writer->SetInput(baseData); IFileWriter::ConfidenceLevel confidenceLevel = writer->GetConfidenceLevel(); if (confidenceLevel == IFileWriter::Unsupported) { continue; } std::string baseDataType = iter->GetProperty(IFileWriter::PROP_BASEDATA_TYPE()).ToString(); auto idxIter = std::find(classHierarchy.begin(), classHierarchy.end(), baseDataType); std::size_t baseDataIndex = std::numeric_limits::max(); if (idxIter != classHierarchy.end()) { baseDataIndex = std::distance(classHierarchy.begin(), idxIter); } Item item; item.d->m_FileWriterRef = *iter; item.d->m_FileWriter = writer; item.d->m_ConfidenceLevel = confidenceLevel; item.d->m_BaseDataIndex = baseDataIndex; item.d->m_MimeType = mimeType; item.d->m_Id = us::any_cast(iter->GetProperty(us::ServiceConstants::SERVICE_ID())); m_Data->m_Items.insert(std::make_pair(item.d->m_Id, item)); m_Data->m_MimeTypes.insert(mimeType); if (!bestItem.GetReference() || bestItem < item) { bestItem = item; } } catch (const us::BadAnyCastException &e) { MITK_WARN << "Unexpected: " << e.what(); } catch (const std::exception &e) { // Log the error but continue MITK_WARN << "IFileWriter::GetConfidenceLevel exception: " << e.what(); } } } } if (bestItem.GetReference()) { m_Data->m_BestId = bestItem.GetServiceId(); m_Data->m_SelectedId = m_Data->m_BestId; } } FileWriterSelector::~FileWriterSelector() {} FileWriterSelector &FileWriterSelector::operator=(const FileWriterSelector &other) { m_Data = other.m_Data; return *this; } bool FileWriterSelector::IsEmpty() const { return m_Data->m_Items.empty(); } std::vector FileWriterSelector::Get(const std::string &mimeType) const { std::vector result; for (std::map::const_iterator iter = m_Data->m_Items.begin(), iterEnd = m_Data->m_Items.end(); iter != iterEnd; ++iter) { if (mimeType.empty() || iter->second.GetMimeType().GetName() == mimeType) { result.push_back(iter->second); } } std::sort(result.begin(), result.end()); return result; } std::vector FileWriterSelector::Get() const { return Get(this->GetSelected().d->m_MimeType.GetName()); } FileWriterSelector::Item FileWriterSelector::Get(long id) const { std::map::const_iterator iter = m_Data->m_Items.find(id); if (iter != m_Data->m_Items.end()) { return iter->second; } return Item(); } FileWriterSelector::Item FileWriterSelector::GetDefault() const { return Get(m_Data->m_BestId); } long FileWriterSelector::GetDefaultId() const { return m_Data->m_BestId; } FileWriterSelector::Item FileWriterSelector::GetSelected() const { return Get(m_Data->m_SelectedId); } long FileWriterSelector::GetSelectedId() const { return m_Data->m_SelectedId; } bool FileWriterSelector::Select(const std::string &mimeType) { std::vector items = Get(mimeType); if (items.empty()) return false; return Select(items.back()); } bool FileWriterSelector::Select(const FileWriterSelector::Item &item) { return Select(item.d->m_Id); } bool FileWriterSelector::Select(long id) { if (id > -1) { if (m_Data->m_Items.find(id) == m_Data->m_Items.end()) { return false; } m_Data->m_SelectedId = id; return true; } return false; } std::vector FileWriterSelector::GetMimeTypes() const { std::vector result; result.reserve(m_Data->m_MimeTypes.size()); result.assign(m_Data->m_MimeTypes.begin(), m_Data->m_MimeTypes.end()); return result; } void FileWriterSelector::Swap(FileWriterSelector &fws) { m_Data.Swap(fws.m_Data); } FileWriterSelector::Item::Item(const FileWriterSelector::Item &other) : d(other.d) {} FileWriterSelector::Item::~Item() {} FileWriterSelector::Item &FileWriterSelector::Item::operator=(const FileWriterSelector::Item &other) { d = other.d; return *this; } IFileWriter *FileWriterSelector::Item::GetWriter() const { return d->m_FileWriter; } std::string FileWriterSelector::Item::GetDescription() const { us::Any descr = d->m_FileWriterRef.GetProperty(IFileWriter::PROP_DESCRIPTION()); if (descr.Empty()) return std::string(); return descr.ToString(); } IFileWriter::ConfidenceLevel FileWriterSelector::Item::GetConfidenceLevel() const { return d->m_ConfidenceLevel; } MimeType FileWriterSelector::Item::GetMimeType() const { return d->m_MimeType; } std::string FileWriterSelector::Item::GetBaseDataType() const { us::Any any = d->m_FileWriterRef.GetProperty(IFileWriter::PROP_BASEDATA_TYPE()); if (any.Empty()) return std::string(); return any.ToString(); } us::ServiceReference FileWriterSelector::Item::GetReference() const { return d->m_FileWriterRef; } long FileWriterSelector::Item::GetServiceId() const { return d->m_Id; } bool FileWriterSelector::Item::operator<(const FileWriterSelector::Item &other) const { // sort by confidence level first (ascending) if (d->m_ConfidenceLevel == other.d->m_ConfidenceLevel) { // sort by class hierarchy index (writers for more derived // based data types are considered a better match) if (d->m_BaseDataIndex == other.d->m_BaseDataIndex) { // sort by file writer service ranking return d->m_FileWriterRef < other.d->m_FileWriterRef; } return other.d->m_BaseDataIndex < d->m_BaseDataIndex; } return d->m_ConfidenceLevel < other.d->m_ConfidenceLevel; } FileWriterSelector::Item::Item() : d(new Impl()) {} void swap(FileWriterSelector &fws1, FileWriterSelector &fws2) { fws1.Swap(fws2); } } diff --git a/Modules/Core/src/IO/mitkIOMimeTypes.cpp b/Modules/Core/src/IO/mitkIOMimeTypes.cpp index 34c558825a..0dd2af91a8 100644 --- a/Modules/Core/src/IO/mitkIOMimeTypes.cpp +++ b/Modules/Core/src/IO/mitkIOMimeTypes.cpp @@ -1,363 +1,365 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkIOMimeTypes.h" #include "mitkCustomMimeType.h" #include "mitkLogMacros.h" +#include #include "itkGDCMImageIO.h" #include "itkMetaDataObject.h" #include #include namespace mitk { IOMimeTypes::BaseDicomMimeType::BaseDicomMimeType(const std::string& name) : CustomMimeType(name) { this->AddExtension("gdcm"); this->AddExtension("dcm"); this->AddExtension("DCM"); this->AddExtension("dc3"); this->AddExtension("DC3"); this->AddExtension("ima"); this->AddExtension("img"); this->SetCategory(CATEGORY_IMAGES()); this->SetComment("DICOM"); } bool IOMimeTypes::BaseDicomMimeType::AppliesTo(const std::string &path) const { // check whether directory or file // if directory try to find first file within it instead - bool pathIsDirectory = itksys::SystemTools::FileIsDirectory(path); + bool pathIsDirectory = itksys::SystemTools::FileIsDirectory(Utf8Util::Local8BitToUtf8(path)); std::string filepath = path; if (pathIsDirectory) { itksys::Directory input; input.Load(path.c_str()); std::vector files; for (unsigned long idx = 0; idxMatchesExtension(filename)) { std::string fullpath = path + "/" + std::string(input.GetFile(idx)); files.push_back(fullpath.c_str()); } } if (!files.empty()) { filepath = files.front(); } } // Ask the GDCM ImageIO class directly itk::GDCMImageIO::Pointer gdcmIO = itk::GDCMImageIO::New(); gdcmIO->SetFileName(filepath); try { gdcmIO->ReadImageInformation(); } catch (const itk::ExceptionObject & /*err*/) { return false; } //DICOMRT modalities have specific reader, don't read with normal DICOM readers std::string modality; itk::MetaDataDictionary& dict = gdcmIO->GetMetaDataDictionary(); itk::ExposeMetaData(dict, "0008|0060", modality); MITK_DEBUG << "DICOM Modality detected by MimeType "<< this->GetName() << " is " << modality; if (modality == "RTSTRUCT" || modality == "RTDOSE" || modality == "RTPLAN") { return false; } else { return gdcmIO->CanReadFile(filepath.c_str()); } } IOMimeTypes::BaseDicomMimeType*IOMimeTypes::BaseDicomMimeType::Clone() const { return new BaseDicomMimeType(*this); } IOMimeTypes::DicomMimeType::DicomMimeType() : BaseDicomMimeType(DICOM_MIMETYPE_NAME()) { } IOMimeTypes::DicomMimeType* IOMimeTypes::DicomMimeType::Clone() const { return new DicomMimeType(*this); } std::vector IOMimeTypes::Get() { std::vector mimeTypes; // order matters here (descending rank for mime types) mimeTypes.push_back(NRRD_MIMETYPE().Clone()); mimeTypes.push_back(NIFTI_MIMETYPE().Clone()); mimeTypes.push_back(VTK_IMAGE_MIMETYPE().Clone()); mimeTypes.push_back(VTK_PARALLEL_IMAGE_MIMETYPE().Clone()); mimeTypes.push_back(VTK_IMAGE_LEGACY_MIMETYPE().Clone()); mimeTypes.push_back(DICOM_MIMETYPE().Clone()); mimeTypes.push_back(VTK_POLYDATA_MIMETYPE().Clone()); mimeTypes.push_back(VTK_PARALLEL_POLYDATA_MIMETYPE().Clone()); mimeTypes.push_back(VTK_POLYDATA_LEGACY_MIMETYPE().Clone()); mimeTypes.push_back(STEREOLITHOGRAPHY_MIMETYPE().Clone()); mimeTypes.push_back(WAVEFRONT_OBJ_MIMETYPE().Clone()); mimeTypes.push_back(STANFORD_PLY_MIMETYPE().Clone()); mimeTypes.push_back(RAW_MIMETYPE().Clone()); mimeTypes.push_back(POINTSET_MIMETYPE().Clone()); return mimeTypes; } CustomMimeType IOMimeTypes::VTK_IMAGE_MIMETYPE() { CustomMimeType mimeType(VTK_IMAGE_NAME()); mimeType.AddExtension("vti"); mimeType.SetCategory(CATEGORY_IMAGES()); mimeType.SetComment("VTK Image"); return mimeType; } CustomMimeType IOMimeTypes::VTK_IMAGE_LEGACY_MIMETYPE() { CustomMimeType mimeType(VTK_IMAGE_LEGACY_NAME()); mimeType.AddExtension("vtk"); mimeType.SetCategory(CATEGORY_IMAGES()); mimeType.SetComment("VTK Legacy Image"); return mimeType; } CustomMimeType IOMimeTypes::VTK_PARALLEL_IMAGE_MIMETYPE() { CustomMimeType mimeType(VTK_PARALLEL_IMAGE_NAME()); mimeType.AddExtension("pvti"); mimeType.SetCategory(CATEGORY_IMAGES()); mimeType.SetComment("VTK Parallel Image"); return mimeType; } CustomMimeType IOMimeTypes::VTK_POLYDATA_MIMETYPE() { CustomMimeType mimeType(VTK_POLYDATA_NAME()); mimeType.AddExtension("vtp"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("VTK PolyData"); return mimeType; } CustomMimeType IOMimeTypes::VTK_POLYDATA_LEGACY_MIMETYPE() { CustomMimeType mimeType(VTK_POLYDATA_LEGACY_NAME()); mimeType.AddExtension("vtk"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("VTK Legacy PolyData"); return mimeType; } CustomMimeType IOMimeTypes::VTK_PARALLEL_POLYDATA_MIMETYPE() { CustomMimeType mimeType(VTK_PARALLEL_POLYDATA_NAME()); mimeType.AddExtension("pvtp"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("VTK Parallel PolyData"); return mimeType; } CustomMimeType IOMimeTypes::STEREOLITHOGRAPHY_MIMETYPE() { CustomMimeType mimeType(STEREOLITHOGRAPHY_NAME()); mimeType.AddExtension("stl"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("Stereolithography"); return mimeType; } CustomMimeType IOMimeTypes::WAVEFRONT_OBJ_MIMETYPE() { CustomMimeType mimeType(WAVEFRONT_OBJ_NAME()); mimeType.AddExtension("obj"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("Wavefront OBJ"); return mimeType; } CustomMimeType IOMimeTypes::STANFORD_PLY_MIMETYPE() { CustomMimeType mimeType(STANFORD_PLY_NAME()); mimeType.AddExtension("ply"); mimeType.SetCategory(CATEGORY_SURFACES()); mimeType.SetComment("Stanford PLY"); return mimeType; } std::string IOMimeTypes::STEREOLITHOGRAPHY_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".stl"; return name; } std::string IOMimeTypes::WAVEFRONT_OBJ_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".obj"; return name; } std::string IOMimeTypes::STANFORD_PLY_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".ply"; return name; } std::string IOMimeTypes::DEFAULT_BASE_NAME() { static std::string name = "application/vnd.mitk"; return name; } std::string IOMimeTypes::CATEGORY_IMAGES() { static std::string cat = "Images"; return cat; } std::string IOMimeTypes::CATEGORY_SURFACES() { static std::string cat = "Surfaces"; return cat; } std::string IOMimeTypes::VTK_IMAGE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.image"; return name; } std::string IOMimeTypes::VTK_IMAGE_LEGACY_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.image.legacy"; return name; } std::string IOMimeTypes::VTK_PARALLEL_IMAGE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.parallel.image"; return name; } std::string IOMimeTypes::VTK_POLYDATA_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.polydata"; return name; } std::string IOMimeTypes::VTK_POLYDATA_LEGACY_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.polydata.legacy"; return name; } std::string IOMimeTypes::VTK_PARALLEL_POLYDATA_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".vtk.parallel.polydata"; return name; } CustomMimeType IOMimeTypes::NRRD_MIMETYPE() { CustomMimeType mimeType(NRRD_MIMETYPE_NAME()); mimeType.AddExtension("nrrd"); mimeType.AddExtension("nhdr"); mimeType.SetCategory("Images"); mimeType.SetComment("NRRD"); return mimeType; } CustomMimeType IOMimeTypes::NIFTI_MIMETYPE() { CustomMimeType mimeType(NIFTI_MIMETYPE_NAME()); mimeType.AddExtension("nii"); mimeType.AddExtension("nii.gz"); mimeType.AddExtension("hdr"); mimeType.AddExtension("hdr.gz"); mimeType.AddExtension("img"); mimeType.AddExtension("img.gz"); mimeType.AddExtension("nia"); mimeType.SetCategory("Images"); mimeType.SetComment("Nifti"); return mimeType; } CustomMimeType IOMimeTypes::RAW_MIMETYPE() { CustomMimeType mimeType(RAW_MIMETYPE_NAME()); mimeType.AddExtension("raw"); mimeType.SetCategory("Images"); mimeType.SetComment("Raw data"); return mimeType; } IOMimeTypes::DicomMimeType IOMimeTypes::DICOM_MIMETYPE() { return DicomMimeType(); } std::string IOMimeTypes::NRRD_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".image.nrrd"; return name; } std::string IOMimeTypes::NIFTI_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".image.nifti"; return name; } std::string IOMimeTypes::RAW_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".image.raw"; return name; } std::string IOMimeTypes::DICOM_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".image.dicom"; return name; } CustomMimeType IOMimeTypes::POINTSET_MIMETYPE() { CustomMimeType mimeType(POINTSET_MIMETYPE_NAME()); mimeType.AddExtension("mps"); mimeType.SetCategory("Point Sets"); mimeType.SetComment("MITK Point Set"); return mimeType; } std::string IOMimeTypes::POINTSET_MIMETYPE_NAME() { static std::string name = DEFAULT_BASE_NAME() + ".pointset"; return name; } CustomMimeType IOMimeTypes::GEOMETRY_DATA_MIMETYPE() { mitk::CustomMimeType mimeType(DEFAULT_BASE_NAME() + ".geometrydata"); mimeType.AddExtension("mitkgeometry"); mimeType.SetCategory("Geometries"); mimeType.SetComment("GeometryData object"); return mimeType; } } diff --git a/Modules/Core/src/IO/mitkIOUtil.cpp b/Modules/Core/src/IO/mitkIOUtil.cpp index 9089e4bd69..5cf1ecba84 100644 --- a/Modules/Core/src/IO/mitkIOUtil.cpp +++ b/Modules/Core/src/IO/mitkIOUtil.cpp @@ -1,1063 +1,991 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkIOUtil.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include // ITK #include // VTK #include #include #include #include #include -#ifdef US_PLATFORM_WINDOWS - -#include - -namespace -{ - std::wstring MultiByteToWideChar(const std::string& mbString, UINT codePage) - { - auto numChars = ::MultiByteToWideChar(codePage, 0, mbString.data(), mbString.size(), nullptr, 0); - - if (0 >= numChars) - mitkThrow() << "Failure to convert multi-byte character string to wide character string"; - - std::wstring wString; - wString.resize(numChars); - - ::MultiByteToWideChar(codePage, 0, mbString.data(), mbString.size(), &wString[0], static_cast(wString.size())); - - return wString; - } - - std::string WideCharToMultiByte(const std::wstring& wString, UINT codePage) - { - auto numChars = ::WideCharToMultiByte(codePage, 0, wString.data(), wString.size(), nullptr, 0, nullptr, nullptr); - - if (0 >= numChars) - mitkThrow() << "Failure to convert wide character string to multi-byte character string"; - - std::string mbString; - mbString.resize(numChars); - - ::WideCharToMultiByte(codePage, 0, wString.data(), wString.size(), &mbString[0], static_cast(mbString.size()), nullptr, nullptr); - - return mbString; - } -} - -#endif - static std::string GetLastErrorStr() { #ifdef US_PLATFORM_POSIX return std::string(strerror(errno)); #else // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; DWORD dw = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, nullptr); std::string errMsg((LPCTSTR)lpMsgBuf); LocalFree(lpMsgBuf); return errMsg; #endif } #ifdef US_PLATFORM_WINDOWS #include #include // make the posix flags point to the obsolte bsd types on windows #define S_IRUSR S_IREAD #define S_IWUSR S_IWRITE #else #include #include #include #endif #include #include static const char validLetters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; // A cross-platform version of the mkstemps function static int mkstemps_compat(char *tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary files to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary file. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return -1; } /* This is where the Xs start. */ char *XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return -1; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, nullptr); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif for (unsigned int count = 0; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; int fd = open(tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); if (fd >= 0) { errno = savedErrno; return fd; } else if (errno != EEXIST) { return -1; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return -1; } // A cross-platform version of the POSIX mkdtemp function static char *mkdtemps_compat(char *tmpl, int suffixlen) { static unsigned long long value = 0; int savedErrno = errno; // Lower bound on the number of temporary dirs to attempt to generate. #define ATTEMPTS_MIN (62 * 62 * 62) /* The number of times to attempt to generate a temporary dir. To conform to POSIX, this must be no smaller than TMP_MAX. */ #if ATTEMPTS_MIN < TMP_MAX const unsigned int attempts = TMP_MAX; #else const unsigned int attempts = ATTEMPTS_MIN; #endif const int len = strlen(tmpl); if ((len - suffixlen) < 6 || strncmp(&tmpl[len - 6 - suffixlen], "XXXXXX", 6)) { errno = EINVAL; return nullptr; } /* This is where the Xs start. */ char *XXXXXX = &tmpl[len - 6 - suffixlen]; /* Get some more or less random data. */ #ifdef US_PLATFORM_WINDOWS { SYSTEMTIME stNow; FILETIME ftNow; // get system time GetSystemTime(&stNow); stNow.wMilliseconds = 500; if (!SystemTimeToFileTime(&stNow, &ftNow)) { errno = -1; return nullptr; } unsigned long long randomTimeBits = ((static_cast(ftNow.dwHighDateTime) << 32) | static_cast(ftNow.dwLowDateTime)); value = randomTimeBits ^ static_cast(GetCurrentThreadId()); } #else { struct timeval tv; gettimeofday(&tv, nullptr); unsigned long long randomTimeBits = ((static_cast(tv.tv_usec) << 32) | static_cast(tv.tv_sec)); value = randomTimeBits ^ static_cast(getpid()); } #endif unsigned int count = 0; for (; count < attempts; value += 7777, ++count) { unsigned long long v = value; /* Fill in the random bits. */ XXXXXX[0] = validLetters[v % 62]; v /= 62; XXXXXX[1] = validLetters[v % 62]; v /= 62; XXXXXX[2] = validLetters[v % 62]; v /= 62; XXXXXX[3] = validLetters[v % 62]; v /= 62; XXXXXX[4] = validLetters[v % 62]; v /= 62; XXXXXX[5] = validLetters[v % 62]; #ifdef US_PLATFORM_WINDOWS int fd = _mkdir(tmpl); //, _S_IREAD | _S_IWRITE | _S_IEXEC); #else int fd = mkdir(tmpl, S_IRUSR | S_IWUSR | S_IXUSR); #endif if (fd >= 0) { errno = savedErrno; return tmpl; } else if (errno != EEXIST) { return nullptr; } } /* We got out of the loop because we ran out of combinations to try. */ errno = EEXIST; return nullptr; } //#endif //************************************************************** // mitk::IOUtil method definitions namespace mitk { struct IOUtil::Impl { struct FixedReaderOptionsFunctor : public ReaderOptionsFunctorBase { FixedReaderOptionsFunctor(const IFileReader::Options &options) : m_Options(options) {} bool operator()(LoadInfo &loadInfo) const override { IFileReader *reader = loadInfo.m_ReaderSelector.GetSelected().GetReader(); if (reader) { reader->SetOptions(m_Options); } return false; } private: const IFileReader::Options &m_Options; }; struct FixedWriterOptionsFunctor : public WriterOptionsFunctorBase { FixedWriterOptionsFunctor(const IFileReader::Options &options) : m_Options(options) {} bool operator()(SaveInfo &saveInfo) const override { IFileWriter *writer = saveInfo.m_WriterSelector.GetSelected().GetWriter(); if (writer) { writer->SetOptions(m_Options); } return false; } private: const IFileWriter::Options &m_Options; }; static BaseData::Pointer LoadBaseDataFromFile(const std::string &path, const ReaderOptionsFunctorBase* optionsCallback = nullptr); }; BaseData::Pointer IOUtil::Impl::LoadBaseDataFromFile(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback) { std::vector baseDataList = Load(path, optionsCallback); // The Load(path) call above should throw an exception if nothing could be loaded assert(!baseDataList.empty()); return baseDataList.front(); } - std::string IOUtil::Local8BitToUtf8(const std::string& local8BitStr) - { -#ifdef US_PLATFORM_WINDOWS - try - { - return WideCharToMultiByte(MultiByteToWideChar(local8BitStr, CP_ACP), CP_UTF8); - } - catch (const mitk::Exception&) - { - MITK_WARN << "String conversion from current code page to UTF-8 failed. Input string is returned unmodified."; - } -#endif - - return local8BitStr; - } - - std::string IOUtil::Utf8ToLocal8Bit(const std::string& utf8Str) - { -#ifdef US_PLATFORM_WINDOWS - try - { - return WideCharToMultiByte(MultiByteToWideChar(utf8Str, CP_UTF8), CP_ACP); - } - catch (const mitk::Exception&) - { - MITK_WARN << "String conversion from UTF-8 to current code page failed. Input string is returned unmodified."; - } -#endif - - return utf8Str; - } - #ifdef US_PLATFORM_WINDOWS std::string IOUtil::GetProgramPath() { char path[512]; std::size_t index = std::string(path, GetModuleFileName(nullptr, path, 512)).find_last_of('\\'); return std::string(path, index); } #elif defined(US_PLATFORM_APPLE) #include std::string IOUtil::GetProgramPath() { char path[512]; uint32_t size = sizeof(path); if (_NSGetExecutablePath(path, &size) == 0) { std::size_t index = std::string(path).find_last_of('/'); std::string strPath = std::string(path, index); // const char* execPath = strPath.c_str(); // mitk::StandardFileLocations::GetInstance()->AddDirectoryForSearch(execPath,false); return strPath; } return std::string(); } #else #include #include #include std::string IOUtil::GetProgramPath() { std::stringstream ss; ss << "/proc/" << getpid() << "/exe"; char proc[512] = {0}; ssize_t ch = readlink(ss.str().c_str(), proc, 512); if (ch == -1) return std::string(); std::size_t index = std::string(proc).find_last_of('/'); return std::string(proc, index); } #endif char IOUtil::GetDirectorySeparator() { #ifdef US_PLATFORM_WINDOWS return '\\'; #else return '/'; #endif } std::string IOUtil::GetTempPath() { static std::string result; if (result.empty()) { #ifdef US_PLATFORM_WINDOWS char tempPathTestBuffer[1]; DWORD bufferLength = ::GetTempPath(1, tempPathTestBuffer); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } std::vector tempPath(bufferLength); bufferLength = ::GetTempPath(bufferLength, &tempPath[0]); if (bufferLength == 0) { mitkThrow() << GetLastErrorStr(); } result.assign(tempPath.begin(), tempPath.begin() + static_cast(bufferLength)); #else result = "/tmp/"; #endif } return result; } std::string IOUtil::CreateTemporaryFile(const std::string &templateName, std::string path) { std::ofstream tmpOutputStream; std::string returnValue = CreateTemporaryFile(tmpOutputStream, templateName, path); tmpOutputStream.close(); return returnValue; } std::string IOUtil::CreateTemporaryFile(std::ofstream &f, const std::string &templateName, std::string path) { return CreateTemporaryFile(f, std::ios_base::out | std::ios_base::trunc, templateName, path); } std::string IOUtil::CreateTemporaryFile(std::ofstream &f, std::ios_base::openmode mode, const std::string &templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? -1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? -1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; int fd = mkstemps_compat(&dst_path[0], suffixlen); if (fd != -1) { path.assign(dst_path.begin(), dst_path.end() - 1); f.open(path.c_str(), mode | std::ios_base::out | std::ios_base::trunc); close(fd); } else { mitkThrow() << "Creating temporary file " << &dst_path[0] << " failed: " << GetLastErrorStr(); } return path; } std::string IOUtil::CreateTemporaryDirectory(const std::string &templateName, std::string path) { if (path.empty()) { path = GetTempPath(); } path += GetDirectorySeparator() + templateName; std::vector dst_path(path.begin(), path.end()); dst_path.push_back('\0'); std::size_t lastX = path.find_last_of('X'); std::size_t firstX = path.find_last_not_of('X', lastX); int firstNonX = firstX == std::string::npos ? -1 : firstX - 1; while (lastX != std::string::npos && (lastX - firstNonX) < 6) { lastX = path.find_last_of('X', firstX); firstX = path.find_last_not_of('X', lastX); firstNonX = firstX == std::string::npos ? -1 : firstX - 1; } std::size_t suffixlen = lastX == std::string::npos ? path.size() : path.size() - lastX - 1; if (mkdtemps_compat(&dst_path[0], suffixlen) == nullptr) { mitkThrow() << "Creating temporary directory " << &dst_path[0] << " failed: " << GetLastErrorStr(); } path.assign(dst_path.begin(), dst_path.end() - 1); return path; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string &path, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback) { std::vector paths; paths.push_back(path); return Load(paths, storage, optionsCallback); } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::string &path, const IFileReader::Options &options, DataStorage &storage) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, nodeResult, &storage, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::string &path, const ReaderOptionsFunctorBase *optionsCallback) { std::vector paths; paths.push_back(path); return Load(paths, optionsCallback); } std::vector IOUtil::Load(const std::string &path, const IFileReader::Options &options) { std::vector loadInfos; loadInfos.push_back(LoadInfo(path)); Impl::FixedReaderOptionsFunctor optionsCallback(options); std::string errMsg = Load(loadInfos, nullptr, nullptr, &optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return loadInfos.front().m_Output; } DataStorage::SetOfObjects::Pointer IOUtil::Load(const std::vector &paths, DataStorage &storage, const ReaderOptionsFunctorBase *optionsCallback) { DataStorage::SetOfObjects::Pointer nodeResult = DataStorage::SetOfObjects::New(); std::vector loadInfos; for (const auto &loadInfo : paths) { - loadInfos.push_back(loadInfo); + loadInfos.emplace_back(loadInfo); } std::string errMsg = Load(loadInfos, nodeResult, &storage, optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } return nodeResult; } std::vector IOUtil::Load(const std::vector &paths, const ReaderOptionsFunctorBase *optionsCallback) { std::vector result; std::vector loadInfos; for (const auto &loadInfo : paths) { - loadInfos.push_back(loadInfo); + loadInfos.emplace_back(loadInfo); } std::string errMsg = Load(loadInfos, nullptr, nullptr, optionsCallback); if (!errMsg.empty()) { mitkThrow() << errMsg; } for (std::vector::const_iterator iter = loadInfos.begin(), iterEnd = loadInfos.end(); iter != iterEnd; ++iter) { result.insert(result.end(), iter->m_Output.begin(), iter->m_Output.end()); } return result; } std::string IOUtil::Load(std::vector &loadInfos, DataStorage::SetOfObjects *nodeResult, DataStorage *ds, const ReaderOptionsFunctorBase *optionsCallback) { if (loadInfos.empty()) { return "No input files given"; } int filesToRead = loadInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2 * filesToRead); std::string errMsg; std::map usedReaderItems; std::vector< std::string > read_files; for (auto &loadInfo : loadInfos) { if(std::find(read_files.begin(), read_files.end(), loadInfo.m_Path) != read_files.end()) continue; std::vector readers = loadInfo.m_ReaderSelector.Get(); if (readers.empty()) { - if (!itksys::SystemTools::FileExists(loadInfo.m_Path.c_str())) + if (!itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(loadInfo.m_Path).c_str())) { errMsg += "File '" + loadInfo.m_Path + "' does not exist\n"; } else { errMsg += "No reader available for '" + loadInfo.m_Path + "'\n"; } continue; } bool callOptionsCallback = readers.size() > 1 || !readers.front().GetReader()->GetOptions().empty(); // check if we already used a reader which should be re-used std::vector currMimeTypes = loadInfo.m_ReaderSelector.GetMimeTypes(); std::string selectedMimeType; for (std::vector::const_iterator mimeTypeIter = currMimeTypes.begin(), mimeTypeIterEnd = currMimeTypes.end(); mimeTypeIter != mimeTypeIterEnd; ++mimeTypeIter) { std::map::const_iterator oldSelectedItemIter = usedReaderItems.find(mimeTypeIter->GetName()); if (oldSelectedItemIter != usedReaderItems.end()) { // we found an already used item for a mime-type which is contained // in the current reader set, check all current readers if there service // id equals the old reader for (std::vector::const_iterator currReaderItem = readers.begin(), currReaderItemEnd = readers.end(); currReaderItem != currReaderItemEnd; ++currReaderItem) { if (currReaderItem->GetMimeType().GetName() == mimeTypeIter->GetName() && currReaderItem->GetServiceId() == oldSelectedItemIter->second.GetServiceId() && currReaderItem->GetConfidenceLevel() >= oldSelectedItemIter->second.GetConfidenceLevel()) { // okay, we used the same reader already, re-use its options selectedMimeType = mimeTypeIter->GetName(); callOptionsCallback = false; loadInfo.m_ReaderSelector.Select(oldSelectedItemIter->second.GetServiceId()); loadInfo.m_ReaderSelector.GetSelected().GetReader()->SetOptions( oldSelectedItemIter->second.GetReader()->GetOptions()); break; } } if (!selectedMimeType.empty()) break; } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(loadInfo); if (!callOptionsCallback && !loadInfo.m_Cancel) { usedReaderItems.erase(selectedMimeType); FileReaderSelector::Item selectedItem = loadInfo.m_ReaderSelector.GetSelected(); usedReaderItems.insert(std::make_pair(selectedItem.GetMimeType().GetName(), selectedItem)); } } if (loadInfo.m_Cancel) { errMsg += "Reading operation(s) cancelled."; break; } IFileReader *reader = loadInfo.m_ReaderSelector.GetSelected().GetReader(); if (reader == nullptr) { errMsg += "Unexpected nullptr reader."; break; } // Do the actual reading try { DataStorage::SetOfObjects::Pointer nodes; if (ds != nullptr) { nodes = reader->Read(*ds); std::vector< std::string > new_files = reader->GetReadFiles(); read_files.insert( read_files.end(), new_files.begin(), new_files.end() ); } else { nodes = DataStorage::SetOfObjects::New(); std::vector baseData = reader->Read(); for (auto iter = baseData.begin(); iter != baseData.end(); ++iter) { if (iter->IsNotNull()) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(*iter); nodes->InsertElement(nodes->Size(), node); } } std::vector< std::string > new_files = reader->GetReadFiles(); read_files.insert( read_files.end(), new_files.begin(), new_files.end() ); } for (DataStorage::SetOfObjects::ConstIterator nodeIter = nodes->Begin(), nodeIterEnd = nodes->End(); nodeIter != nodeIterEnd; ++nodeIter) { const mitk::DataNode::Pointer &node = nodeIter->Value(); mitk::BaseData::Pointer data = node->GetData(); if (data.IsNull()) { continue; } - auto path = Local8BitToUtf8(loadInfo.m_Path); - auto pathProp = mitk::StringProperty::New(path); - data->SetProperty("path", pathProp); + data->SetProperty("path", mitk::StringProperty::New(Utf8Util::Local8BitToUtf8(loadInfo.m_Path))); loadInfo.m_Output.push_back(data); if (nodeResult) { nodeResult->push_back(nodeIter->Value()); } } if (loadInfo.m_Output.empty() || (nodeResult && nodeResult->Size() == 0)) { errMsg += "Unknown read error occurred reading " + loadInfo.m_Path; } } catch (const std::exception &e) { errMsg += "Exception occured when reading file " + loadInfo.m_Path + ":\n" + e.what() + "\n\n"; } mitk::ProgressBar::GetInstance()->Progress(2); --filesToRead; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2 * filesToRead); return errMsg; } std::vector IOUtil::Load(const us::ModuleResource &usResource, std::ios_base::openmode mode) { us::ModuleResourceStream resStream(usResource, mode); mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); std::vector mimetypes = mimeTypeProvider->GetMimeTypesForFile(usResource.GetResourcePath()); std::vector data; if (mimetypes.empty()) { mitkThrow() << "No mimetype for resource stream: " << usResource.GetResourcePath(); return data; } mitk::FileReaderRegistry fileReaderRegistry; std::vector> refs = fileReaderRegistry.GetReferences(mimetypes[0]); if (refs.empty()) { mitkThrow() << "No reader available for resource stream: " << usResource.GetResourcePath(); return data; } mitk::IFileReader *reader = fileReaderRegistry.GetReader(refs[0]); reader->SetInput(usResource.GetResourcePath(), &resStream); data = reader->Read(); return data; } void IOUtil::Save(const BaseData *data, const std::string &path, bool setPathProperty) { Save(data, path, IFileWriter::Options(), setPathProperty); } void IOUtil::Save(const BaseData *data, const std::string &path, const IFileWriter::Options &options, bool setPathProperty) { Save(data, std::string(), path, options, setPathProperty); } void IOUtil::Save(const BaseData *data, const std::string &mimeType, const std::string &path, bool addExtension, bool setPathProperty) { Save(data, mimeType, path, IFileWriter::Options(), addExtension, setPathProperty); } void IOUtil::Save(const BaseData *data, const std::string &mimeType, const std::string &path, const IFileWriter::Options &options, bool addExtension, bool setPathProperty) { if ((data == nullptr) || (data->IsEmpty())) mitkThrow() << "BaseData cannotbe null or empty for save methods in IOUtil.h."; std::string errMsg; if (options.empty()) { errMsg = Save(data, mimeType, path, nullptr, addExtension, setPathProperty); } else { Impl::FixedWriterOptionsFunctor optionsCallback(options); errMsg = Save(data, mimeType, path, &optionsCallback, addExtension, setPathProperty); } if (!errMsg.empty()) { mitkThrow() << errMsg; } } void IOUtil::Save(std::vector &saveInfos, bool setPathProperty) { std::string errMsg = Save(saveInfos, nullptr, setPathProperty); if (!errMsg.empty()) { mitkThrow() << errMsg; } } std::string IOUtil::Save(const BaseData *data, const std::string &mimeTypeName, const std::string &path, WriterOptionsFunctorBase *optionsCallback, bool addExtension, bool setPathProperty) { if (path.empty()) { return "No output filename given"; } mitk::CoreServicePointer mimeTypeProvider(mitk::CoreServices::GetMimeTypeProvider()); MimeType mimeType = mimeTypeProvider->GetMimeTypeForName(mimeTypeName); SaveInfo saveInfo(data, mimeType, path); - std::string ext = itksys::SystemTools::GetFilenameExtension(path); + std::string ext = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(path))); if (saveInfo.m_WriterSelector.IsEmpty()) { return std::string("No suitable writer found for the current data of type ") + data->GetNameOfClass() + (mimeType.IsValid() ? (std::string(" and mime-type ") + mimeType.GetName()) : std::string()) + (ext.empty() ? std::string() : (std::string(" with extension ") + ext)); } // Add an extension if not already specified if (ext.empty() && addExtension) { ext = saveInfo.m_MimeType.GetExtensions().empty() ? std::string() : "." + saveInfo.m_MimeType.GetExtensions().front(); saveInfo.m_Path += ext; } std::vector infos; infos.push_back(saveInfo); return Save(infos, optionsCallback, setPathProperty); } std::string IOUtil::Save(std::vector &saveInfos, WriterOptionsFunctorBase *optionsCallback, bool setPathProperty) { if (saveInfos.empty()) { return "No data for saving available"; } int filesToWrite = saveInfos.size(); mitk::ProgressBar::GetInstance()->AddStepsToDo(2 * filesToWrite); std::string errMsg; std::set usedSaveInfos; for (auto &saveInfo : saveInfos) { const std::string baseDataType = saveInfo.m_BaseData->GetNameOfClass(); std::vector writers = saveInfo.m_WriterSelector.Get(); // Error out if no compatible Writer was found if (writers.empty()) { errMsg += std::string("No writer available for ") + baseDataType + " data.\n"; continue; } bool callOptionsCallback = writers.size() > 1 || !writers[0].GetWriter()->GetOptions().empty(); // check if we already used a writer for this base data type // which should be re-used auto oldSaveInfoIter = usedSaveInfos.find(saveInfo); if (oldSaveInfoIter != usedSaveInfos.end()) { // we previously saved a base data object of the same data with the same mime-type, // check if the same writer is contained in the current writer set and if the // confidence level matches FileWriterSelector::Item oldSelectedItem = oldSaveInfoIter->m_WriterSelector.Get(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); for (std::vector::const_iterator currWriterItem = writers.begin(), currWriterItemEnd = writers.end(); currWriterItem != currWriterItemEnd; ++currWriterItem) { if (currWriterItem->GetServiceId() == oldSelectedItem.GetServiceId() && currWriterItem->GetConfidenceLevel() >= oldSelectedItem.GetConfidenceLevel()) { // okay, we used the same writer already, re-use its options callOptionsCallback = false; saveInfo.m_WriterSelector.Select(oldSaveInfoIter->m_WriterSelector.GetSelectedId()); saveInfo.m_WriterSelector.GetSelected().GetWriter()->SetOptions(oldSelectedItem.GetWriter()->GetOptions()); break; } } } if (callOptionsCallback && optionsCallback) { callOptionsCallback = (*optionsCallback)(saveInfo); if (!callOptionsCallback && !saveInfo.m_Cancel) { usedSaveInfos.erase(saveInfo); usedSaveInfos.insert(saveInfo); } } if (saveInfo.m_Cancel) { errMsg += "Writing operation(s) cancelled."; break; } IFileWriter *writer = saveInfo.m_WriterSelector.GetSelected().GetWriter(); if (writer == nullptr) { errMsg += "Unexpected nullptr writer."; break; } // Do the actual writing try { writer->SetOutputLocation(saveInfo.m_Path); writer->Write(); } catch (const std::exception &e) { errMsg += std::string("Exception occurred when writing to ") + saveInfo.m_Path + ":\n" + e.what() + "\n"; } if (setPathProperty) - saveInfo.m_BaseData->GetPropertyList()->SetStringProperty("path", Local8BitToUtf8(saveInfo.m_Path).c_str()); + saveInfo.m_BaseData->GetPropertyList()->SetStringProperty("path", Utf8Util::Local8BitToUtf8(saveInfo.m_Path).c_str()); mitk::ProgressBar::GetInstance()->Progress(2); --filesToWrite; } if (!errMsg.empty()) { MITK_ERROR << errMsg; } mitk::ProgressBar::GetInstance()->Progress(2 * filesToWrite); return errMsg; } IOUtil::SaveInfo::SaveInfo(const BaseData *baseData, const MimeType &mimeType, const std::string &path) : m_BaseData(baseData), m_WriterSelector(baseData, mimeType.GetName(), path), m_MimeType(mimeType.IsValid() ? mimeType // use the original mime-type : (m_WriterSelector.IsEmpty() ? mimeType // no writer found, use the original invalid mime-type : m_WriterSelector.GetDefault().GetMimeType() // use the found default mime-type )), m_Path(path), m_Cancel(false) { } bool IOUtil::SaveInfo::operator<(const IOUtil::SaveInfo &other) const { int r = strcmp(m_BaseData->GetNameOfClass(), other.m_BaseData->GetNameOfClass()); if (r == 0) { return m_WriterSelector.GetSelected().GetMimeType() < other.m_WriterSelector.GetSelected().GetMimeType(); } return r < 0; } IOUtil::LoadInfo::LoadInfo(const std::string &path) : m_Path(path), m_ReaderSelector(path), m_Cancel(false) {} } diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp index dcf50ecff8..e4672cba4a 100644 --- a/Modules/Core/src/IO/mitkItkImageIO.cpp +++ b/Modules/Core/src/IO/mitkItkImageIO.cpp @@ -1,754 +1,754 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkItkImageIO.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; const char* const PROPERTY_KEY_UID = "org_mitk_uid"; ItkImageIO::ItkImageIO(const ItkImageIO &other) : AbstractFileIO(other), m_ImageIO(dynamic_cast(other.m_ImageIO->Clone().GetPointer())) { this->InitializeDefaultMetaDataKeys(); } std::vector ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName) { std::vector extensions; // Try to fix-up some known ITK image IO classes if (imageIOName == "GiplImageIO") { extensions.push_back("gipl"); extensions.push_back("gipl.gz"); } else if (imageIOName == "GDCMImageIO") { extensions.push_back("gdcm"); extensions.push_back("dcm"); extensions.push_back("DCM"); extensions.push_back("dc3"); extensions.push_back("DC3"); extensions.push_back("ima"); extensions.push_back("img"); } else if (imageIOName == "PNGImageIO") { extensions.push_back("png"); extensions.push_back("PNG"); } else if (imageIOName == "StimulateImageIO") { extensions.push_back("spr"); } else if (imageIOName == "HDF5ImageIO") { extensions.push_back("hdf"); extensions.push_back("h4"); extensions.push_back("hdf4"); extensions.push_back("h5"); extensions.push_back("hdf5"); extensions.push_back("he4"); extensions.push_back("he5"); extensions.push_back("hd5"); } else if ("GE4ImageIO" == imageIOName || "GE5ImageIO" == imageIOName || "Bruker2dseqImageIO" == imageIOName) { extensions.push_back(""); } if (!extensions.empty()) { MITK_DEBUG << "Fixing up known extensions for " << imageIOName; } return extensions; } void ItkImageIO::FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType) { if ("GE4ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge4"); } else if ("GE5ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge5"); } else if ("Bruker2dseqImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "bruker2dseq"); } } ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO) : AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); std::vector readExtensions = m_ImageIO->GetSupportedReadExtensions(); if (readExtensions.empty()) { std::string imageIOName = m_ImageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions"; readExtensions = FixUpImageIOExtensions(imageIOName); } CustomMimeType customReaderMimeType; customReaderMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customReaderMimeType.AddExtension(extension); } auto extensions = customReaderMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customReaderMimeType); } this->AbstractFileReader::SetMimeType(customReaderMimeType); std::vector writeExtensions = imageIO->GetSupportedWriteExtensions(); if (writeExtensions.empty()) { std::string imageIOName = imageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions"; writeExtensions = FixUpImageIOExtensions(imageIOName); } if (writeExtensions != readExtensions) { CustomMimeType customWriterMimeType; customWriterMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customWriterMimeType.AddExtension(extension); } auto extensions = customWriterMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customWriterMimeType); } this->AbstractFileWriter::SetMimeType(customWriterMimeType); } std::string description = std::string("ITK ") + imageIO->GetNameOfClass(); this->SetReaderDescription(description); this->SetWriterDescription(description); this->RegisterService(); } ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank) : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); if (rank) { this->AbstractFileReader::SetRanking(rank); this->AbstractFileWriter::SetRanking(rank); } this->RegisterService(); } std::vector ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data) { const auto* timeGeometryTimeData = dynamic_cast*>(data); std::vector result; if (timeGeometryTimeData) { std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue(); std::stringstream stream(dataStr); TimePointType tp; while (stream >> tp) { result.push_back(tp); } } return result; }; itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry) { std::stringstream stream; stream << timeGeometry->GetTimeBounds(0)[0]; const auto maxTimePoints = timeGeometry->CountTimeSteps(); for (TimeStepType pos = 0; pos < maxTimePoints; ++pos) { auto timeBounds = timeGeometry->GetTimeBounds(pos); /////////////////////////////////////// // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. // This workarround should be removed as soon as T28262 is solved! if (pos + 1 == maxTimePoints && timeBounds[0]==timeBounds[1]) { timeBounds[1] = timeBounds[0] + 1.; } // End of workarround for T27883 ////////////////////////////////////// stream << " " << timeBounds[1]; } auto result = itk::MetaDataObject::New(); result->SetMetaDataObjectValue(stream.str()); return result.GetPointer(); }; std::vector ItkImageIO::DoRead() { std::vector result; mitk::LocaleSwitch localeSwitch("C"); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. m_ImageIO->SetFileName(path); m_ImageIO->ReadImageInformation(); unsigned int ndim = m_ImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = m_ImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = m_ImageIO->GetDimensions(i); spacing[i] = m_ImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = m_ImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; m_ImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read(buffer); image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary(); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = m_ImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData = nullptr; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string &key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key); auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info auto newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast *>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); if (loadedProp.IsNull()) { MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info; break; } image->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey(false); for (const auto &defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } // Handle UID if (dictionary.HasKey(PROPERTY_KEY_UID)) { itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); if (uidData.IsNotNull()) { mitk::UIDManipulator uidManipulator(image); uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); } } MITK_INFO << "...finished!"; result.push_back(image.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const { return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported; } void ItkImageIO::Write() { const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { mitkThrow() << "Cannot write non-image data"; } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = image->GetGeometry()->Clone(); // Check if geometry information will be lost if (image->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = image->GetDimension(); const unsigned int *const dimensions = image->GetDimensions(); const mitk::PixelType pixelType = image->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO m_ImageIO->SetNumberOfDimensions(dimension); m_ImageIO->SetPixelType(pixelType.GetPixelType()); - m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? - static_cast(pixelType.GetComponentType()) : - itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); + m_ImageIO->SetComponentType(static_cast(pixelType.GetComponentType()) < PixelComponentUserType + ? pixelType.GetComponentType() + : itk::IOComponentEnum::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)); + mitk::Vector3D mitkDirection(0.0); + mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref()); 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 = mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING; try { value = infoList.front()->GetSerializationFunction()(property.second); } catch (const std::exception& e) { MITK_ERROR << "Error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first << ". Reason: " << e.what(); } catch (...) { MITK_ERROR << "Unkown error when serializing content of property. This often indicates the use of an out dated reader. Property will not be stored. Skipped property: " << property.first; } if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), key, value); } // Handle UID itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_UID, image->GetUID()); ImageReadAccessor imageAccess(image); LocaleSwitch localeSwitch2("C"); m_ImageIO->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } } AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const { // Check if the image dimension is supported const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { // We cannot write a null object, DUH! return IFileWriter::Unsupported; } if (!m_ImageIO->SupportsDimension(image->GetDimension())) { // okay, dimension is not supported. We have to look at a special case: // 3D-Image with one slice. We can treat that as a 2D image. if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1)) return IFileWriter::Supported; else return IFileWriter::Unsupported; } // Check if geometry information will be lost if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { return IFileWriter::PartiallySupported; } return IFileWriter::Supported; } ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); } void ItkImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); } } diff --git a/Modules/Core/src/IO/mitkLog.cpp b/Modules/Core/src/IO/mitkLog.cpp index de655bfa80..64fbe3a00a 100644 --- a/Modules/Core/src/IO/mitkLog.cpp +++ b/Modules/Core/src/IO/mitkLog.cpp @@ -1,249 +1,249 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 -static itk::SimpleFastMutexLock logMutex; +static std::mutex logMutex; static mitk::LoggingBackend *mitkLogBackend = nullptr; static std::ofstream *logFile = nullptr; static std::string logFileName = ""; static std::stringstream *outputWindow = nullptr; static bool logOutputWindow = false; void mitk::LoggingBackend::EnableAdditionalConsoleWindow(bool enable) { logOutputWindow = enable; } void mitk::LoggingBackend::ProcessMessage(const mbilog::LogMessage &l) { - logMutex.Lock(); + logMutex.lock(); #ifdef _WIN32 FormatSmart(l, (int)GetCurrentThreadId()); #else FormatSmart(l); #endif if (logFile) { #ifdef _WIN32 FormatFull(*logFile, l, (int)GetCurrentThreadId()); #else FormatFull(*logFile, l); #endif } if (logOutputWindow) { if (outputWindow == nullptr) { outputWindow = new std::stringstream(); } outputWindow->str(""); outputWindow->clear(); #ifdef _WIN32 FormatFull(*outputWindow, l, (int)GetCurrentThreadId()); #else FormatFull(*outputWindow, l); #endif itk::OutputWindow::GetInstance()->DisplayText(outputWindow->str().c_str()); } - logMutex.Unlock(); + logMutex.unlock(); } void mitk::LoggingBackend::Register() { if (mitkLogBackend) return; mitkLogBackend = new mitk::LoggingBackend(); mbilog::RegisterBackend(mitkLogBackend); } void mitk::LoggingBackend::Unregister() { if (mitkLogBackend) { SetLogFile(nullptr); mbilog::UnregisterBackend(mitkLogBackend); delete mitkLogBackend; mitkLogBackend = nullptr; } } void mitk::LoggingBackend::SetLogFile(const char *file) { // closing old logfile { bool closed = false; std::string closedFileName; - logMutex.Lock(); + logMutex.lock(); if (logFile) { closed = true; closedFileName = logFileName; logFile->close(); delete logFile; logFile = nullptr; logFileName = ""; } - logMutex.Unlock(); + logMutex.unlock(); if (closed) { MITK_INFO << "closing logfile (" << closedFileName << ")"; } } // opening new logfile if (file) { - logMutex.Lock(); + logMutex.lock(); logFileName = file; logFile = new std::ofstream(); logFile->open(file, std::ios_base::out | std::ios_base::app); if (logFile->good()) { - logMutex.Unlock(); + logMutex.unlock(); MITK_INFO << "Logfile: " << logFileName; } else { delete logFile; logFile = nullptr; - logMutex.Unlock(); + logMutex.unlock(); MITK_WARN << "opening logfile '" << file << "' for writing failed"; } // mutex is now unlocked } } std::string mitk::LoggingBackend::GetLogFile() { return logFileName; } void mitk::LoggingBackend::CatchLogFileCommandLineParameter(int &argc, char **argv) { int r; for (r = 1; r < argc; r++) { if (std::string(argv[r]) == "--logfile") { if (r + 1 >= argc) { --argc; MITK_ERROR << "--logfile parameter found, but no file given"; return; } mitk::LoggingBackend::SetLogFile(argv[r + 1]); for (r += 2; r < argc; r++) argv[r - 2] = argv[r]; argc -= 2; return; } } } void mitk::LoggingBackend::RotateLogFiles(const std::string &prefixPath) { static const int numLogFiles = 10; std::string newEmptyLogFileName; // first: rotate the old log files to get a new, free logfile name newEmptyLogFileName = IncrementLogFileNames(prefixPath, numLogFiles); // now: use the new empty logfile name as name for this run mitk::LoggingBackend::SetLogFile(newEmptyLogFileName.c_str()); } std::string mitk::LoggingBackend::IncrementLogFileNames(const std::string &prefixPath, int numLogFiles) { // delete last one { std::stringstream s; s << prefixPath.c_str() << "-" << numLogFiles - 1 << ".log"; // check if the file exists if (CheckIfFileExists(s.str())) // if yes: delete it { int retVal = ::remove(s.str().c_str()); if (retVal != 0) { mitkThrow() << "Problem while deleting the oldest log file. Maybe the access to this files is blocked. Aborting!"; } } } // rename the others for (int r = numLogFiles - 1; r >= 1; r--) { std::stringstream dst; dst << prefixPath.c_str() << "-" << r << ".log"; std::stringstream src; src << prefixPath.c_str() << "-" << r - 1 << ".log"; // check if the source exists if (CheckIfFileExists(src.str())) // if yes: rename it { int retVal = ::rename(src.str().c_str(), dst.str().c_str()); if (retVal != 0) { mitkThrow() << "Problem while renaming the log files. Maybe the access to this files is blocked. Aborting!"; } } } // create new empty name and return it { std::stringstream s; s << prefixPath.c_str() << "-0.log"; return s.str(); } } bool mitk::LoggingBackend::CheckIfFileExists(const std::string &filename) { bool returnValue = false; std::ifstream File(filename.c_str()); if (File.good()) { returnValue = true; } else { returnValue = false; } File.close(); return returnValue; } mbilog::OutputType mitk::LoggingBackend::GetOutputType() const { return mbilog::Console; } diff --git a/Modules/Core/src/IO/mitkPixelType.cpp b/Modules/Core/src/IO/mitkPixelType.cpp index 93c47fee9e..0d4c304d91 100644 --- a/Modules/Core/src/IO/mitkPixelType.cpp +++ b/Modules/Core/src/IO/mitkPixelType.cpp @@ -1,178 +1,178 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkPixelType.h" #include mitk::PixelType::PixelType(const mitk::PixelType &other) : m_ComponentType(other.m_ComponentType), m_PixelType(other.m_PixelType), m_ComponentTypeName(other.m_ComponentTypeName), m_PixelTypeName(other.m_PixelTypeName), m_NumberOfComponents(other.m_NumberOfComponents), m_BytesPerComponent(other.m_BytesPerComponent) { } mitk::PixelType &mitk::PixelType::operator=(const PixelType &other) { m_ComponentType = other.m_ComponentType; m_PixelType = other.m_PixelType; m_ComponentTypeName = other.m_ComponentTypeName; m_PixelTypeName = other.m_PixelTypeName; m_NumberOfComponents = other.m_NumberOfComponents; m_BytesPerComponent = other.m_BytesPerComponent; return *this; } -itk::ImageIOBase::IOPixelType mitk::PixelType::GetPixelType() const +mitk::PixelType::ItkIOPixelType mitk::PixelType::GetPixelType() const { return m_PixelType; } -int mitk::PixelType::GetComponentType() const +mitk::PixelType::ItkIOComponentType mitk::PixelType::GetComponentType() const { return m_ComponentType; } std::string mitk::PixelType::GetPixelTypeAsString() const { return m_PixelTypeName; } std::string mitk::PixelType::GetComponentTypeAsString() const { return m_ComponentTypeName; } std::string mitk::PixelType::GetTypeAsString() const { return m_PixelTypeName + " (" + m_ComponentTypeName + ")"; } size_t mitk::PixelType::GetSize() const { return (m_NumberOfComponents * m_BytesPerComponent); } size_t mitk::PixelType::GetBpe() const { return this->GetSize() * 8; } size_t mitk::PixelType::GetNumberOfComponents() const { return m_NumberOfComponents; } size_t mitk::PixelType::GetBitsPerComponent() const { return m_BytesPerComponent * 8; } mitk::PixelType::~PixelType() { } -mitk::PixelType::PixelType(const int componentType, - const ItkIOPixelType pixelType, +mitk::PixelType::PixelType(ItkIOComponentType componentType, + ItkIOPixelType pixelType, std::size_t bytesPerComponent, std::size_t numberOfComponents, const std::string &componentTypeName, const std::string &pixelTypeName) : m_ComponentType(componentType), m_PixelType(pixelType), m_ComponentTypeName(componentTypeName), m_PixelTypeName(pixelTypeName), m_NumberOfComponents(numberOfComponents), m_BytesPerComponent(bytesPerComponent) { } bool mitk::PixelType::operator==(const mitk::PixelType &rhs) const { bool returnValue = ( this->m_PixelType == rhs.m_PixelType && this->m_ComponentType == rhs.m_ComponentType && this->m_NumberOfComponents == rhs.m_NumberOfComponents && this->m_BytesPerComponent == rhs.m_BytesPerComponent ); MITK_DEBUG << "|> mitk::PixelType::operator== rhs, lhs: \n" << "| m_BytesPerComponent = " << m_BytesPerComponent << ", " << rhs.m_BytesPerComponent << '\n' << "| m_NumberOfComponents = " << m_NumberOfComponents << ", " << rhs.m_NumberOfComponents << '\n' << "| m_PixelTypeName = " << m_PixelTypeName << ", " << rhs.m_PixelTypeName << '\n' << "| m_ComponentTypeName = " << m_ComponentTypeName << ", " << rhs.m_ComponentTypeName << '\n' << "| m_PixelType = " << m_PixelType << ", " << rhs.m_PixelType << '\n' << "| m_ComponentType = " << m_ComponentType << ", " << rhs.m_ComponentType << ", returnValue = " << returnValue << (returnValue ? "[True]" : "[False]") << ". <|"; return returnValue; } bool mitk::PixelType::operator!=(const mitk::PixelType &rhs) const { return !(this->operator==(rhs)); } mitk::PixelType mitk::MakePixelType(vtkImageData *vtkimagedata) { int numOfComponents = vtkimagedata->GetNumberOfScalarComponents(); switch (vtkimagedata->GetScalarType()) { case VTK_BIT: case VTK_CHAR: return mitk::MakePixelType(numOfComponents); break; case VTK_UNSIGNED_CHAR: return mitk::MakePixelType(numOfComponents); break; case VTK_SHORT: return mitk::MakePixelType(numOfComponents); break; case VTK_UNSIGNED_SHORT: return mitk::MakePixelType(numOfComponents); break; case VTK_INT: return mitk::MakePixelType(numOfComponents); break; case VTK_UNSIGNED_INT: return mitk::MakePixelType(numOfComponents); break; case VTK_LONG: return mitk::MakePixelType(numOfComponents); break; case VTK_UNSIGNED_LONG: return mitk::MakePixelType(numOfComponents); break; case VTK_FLOAT: return mitk::MakePixelType(numOfComponents); break; case VTK_DOUBLE: return mitk::MakePixelType(numOfComponents); break; default: break; } mitkThrow() << "tried to make pixeltype from vtkimage of unknown data type(short, char, int, ...)"; } diff --git a/Modules/Core/src/IO/mitkRawImageFileReader.h b/Modules/Core/src/IO/mitkRawImageFileReader.h index 3ad97d9199..8e80383d4d 100644 --- a/Modules/Core/src/IO/mitkRawImageFileReader.h +++ b/Modules/Core/src/IO/mitkRawImageFileReader.h @@ -1,54 +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 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> 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/mitkStandardFileLocations.cpp b/Modules/Core/src/IO/mitkStandardFileLocations.cpp index d92045120e..07b069fece 100644 --- a/Modules/Core/src/IO/mitkStandardFileLocations.cpp +++ b/Modules/Core/src/IO/mitkStandardFileLocations.cpp @@ -1,238 +1,239 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include +#include #include mitk::StandardFileLocations::StandardFileLocations() { } mitk::StandardFileLocations::~StandardFileLocations() { } mitk::StandardFileLocations *mitk::StandardFileLocations::GetInstance() { static StandardFileLocations::Pointer m_Instance = nullptr; if (m_Instance.IsNull()) m_Instance = StandardFileLocations::New(); return m_Instance; } void mitk::StandardFileLocations::AddDirectoryForSearch(const char *dir, bool insertInFrontOfSearchList) { // Do nothing if directory is already included into search list (TODO more clever: search only once!) FileSearchVectorType::iterator iter; if (m_SearchDirectories.size() > 0) { iter = std::find(m_SearchDirectories.begin(), m_SearchDirectories.end(), std::string(dir)); if (iter != m_SearchDirectories.end()) return; } // insert dir into queue if (insertInFrontOfSearchList) { auto it = m_SearchDirectories.begin(); m_SearchDirectories.insert(it, std::string(dir)); } else m_SearchDirectories.push_back(std::string(dir)); } void mitk::StandardFileLocations::RemoveDirectoryForSearch(const char *dir) { FileSearchVectorType::iterator it; // background layers if (m_SearchDirectories.size() > 0) { it = std::find(m_SearchDirectories.begin(), m_SearchDirectories.end(), std::string(dir)); if (it != m_SearchDirectories.end()) { m_SearchDirectories.erase(it); return; } } } std::string mitk::StandardFileLocations::SearchDirectoriesForFile(const char *filename) { FileSearchVectorType::iterator it; for (it = m_SearchDirectories.begin(); it != m_SearchDirectories.end(); ++it) { std::string currDir = (*it); // Perhaps append "/" before appending filename if (currDir.find_last_of("\\") + 1 != currDir.size() || currDir.find_last_of("/") + 1 != currDir.size()) currDir += "/"; // Append filename currDir += filename; // Perhaps remove "/" after filename if (currDir.find_last_of("\\") + 1 == currDir.size() || currDir.find_last_of("/") + 1 == currDir.size()) currDir.erase(currDir.size() - 1, currDir.size()); // convert to OS dependent path schema - currDir = itksys::SystemTools::ConvertToOutputPath(currDir.c_str()); + currDir = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::ConvertToOutputPath(Utf8Util::Local8BitToUtf8(currDir).c_str())); // On windows systems, the ConvertToOutputPath method quotes pathes that contain empty spaces. // These quotes are not expected by the FileExists method and therefore removed, if existing. if (currDir.find_last_of("\"") + 1 == currDir.size()) currDir.erase(currDir.size() - 1, currDir.size()); if (currDir.find_last_of("\"") == 0) currDir.erase(0, 1); // Return first found path - if (itksys::SystemTools::FileExists(currDir.c_str())) + if (itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(currDir).c_str())) return currDir; } return std::string(""); } std::string mitk::StandardFileLocations::FindFile(const char *filename, const char *pathInSourceDir) { std::string directoryPath; // 1. look for MITKCONF environment variable const char *mitkConf = itksys::SystemTools::GetEnv("MITKCONF"); if (mitkConf != nullptr) AddDirectoryForSearch(mitkConf, false); // 2. use .mitk-subdirectory in home directory of the user #if defined(_WIN32) && !defined(__CYGWIN__) const char *homeDrive = itksys::SystemTools::GetEnv("HOMEDRIVE"); const char *homePath = itksys::SystemTools::GetEnv("HOMEPATH"); if ((homeDrive != nullptr) || (homePath != nullptr)) { directoryPath = homeDrive; directoryPath += homePath; directoryPath += "/.mitk/"; AddDirectoryForSearch(directoryPath.c_str(), false); } #else const char *homeDirectory = itksys::SystemTools::GetEnv("HOME"); if (homeDirectory != nullptr) { directoryPath = homeDirectory; directoryPath += "/.mitk/"; AddDirectoryForSearch(directoryPath.c_str(), false); } #endif // defined(_WIN32) && !defined(__CYGWIN__) // 3. look in the current working directory directoryPath = ""; AddDirectoryForSearch(directoryPath.c_str()); - directoryPath = itksys::SystemTools::GetCurrentWorkingDirectory(); + directoryPath = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory()); AddDirectoryForSearch(directoryPath.c_str(), false); std::string directoryBinPath = directoryPath + "/bin"; AddDirectoryForSearch(directoryBinPath.c_str(), false); // 4. use a source tree location from compile time directoryPath = MITK_ROOT; if (pathInSourceDir) { directoryPath += pathInSourceDir; } directoryPath += '/'; AddDirectoryForSearch(directoryPath.c_str(), false); return SearchDirectoriesForFile(filename); } std::string mitk::StandardFileLocations::GetOptionDirectory() { const char *mitkoptions = itksys::SystemTools::GetEnv("MITKOPTIONS"); std::string optionsDirectory; if (mitkoptions != nullptr) { // 1. look for MITKOPTIONS environment variable optionsDirectory = mitkoptions; optionsDirectory += "/"; } else { // 2. use .mitk-subdirectory in home directory of the user std::string homeDirectory; #if defined(_WIN32) && !defined(__CYGWIN__) const char *homeDrive = itksys::SystemTools::GetEnv("HOMEDRIVE"); const char *homePath = itksys::SystemTools::GetEnv("HOMEPATH"); if ((homeDrive == nullptr) || (homePath == nullptr)) { itkGenericOutputMacro(<< "Environment variables HOMEDRIVE and/or HOMEPATH not set" << ". Using current working directory as home directory: " - << itksys::SystemTools::GetCurrentWorkingDirectory()); - homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory(); + << Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory())); + homeDirectory = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory()); } else { homeDirectory = homeDrive; homeDirectory += homePath; } - if (itksys::SystemTools::FileExists(homeDirectory.c_str()) == false) + if (itksys::SystemTools::FileExists(Utf8Util::Local8BitToUtf8(homeDirectory).c_str()) == false) { itkGenericOutputMacro(<< "Could not find home directory at " << homeDirectory << ". Using current working directory as home directory: " - << itksys::SystemTools::GetCurrentWorkingDirectory()); - homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory(); + << Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory())); + homeDirectory = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory()); } #else const char *home = itksys::SystemTools::GetEnv("HOME"); if (home == nullptr) { itkGenericOutputMacro(<< "Environment variable HOME not set" << ". Using current working directory as home directory: " << itksys::SystemTools::GetCurrentWorkingDirectory()); homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory(); } else homeDirectory = home; if (itksys::SystemTools::FileExists(homeDirectory.c_str()) == false) { itkGenericOutputMacro(<< "Could not find home directory at " << homeDirectory << ". Using current working directory as home directory: " << itksys::SystemTools::GetCurrentWorkingDirectory()); homeDirectory = itksys::SystemTools::GetCurrentWorkingDirectory(); } #endif // defined(_WIN32) && !defined(__CYGWIN__) optionsDirectory = homeDirectory; optionsDirectory += "/.mitk"; } - optionsDirectory = itksys::SystemTools::ConvertToOutputPath(optionsDirectory.c_str()); + optionsDirectory = itksys::SystemTools::ConvertToOutputPath(Utf8Util::Local8BitToUtf8(optionsDirectory).c_str()); if (itksys::SystemTools::CountChar(optionsDirectory.c_str(), '"') > 0) { char *unquoted = itksys::SystemTools::RemoveChars(optionsDirectory.c_str(), "\""); optionsDirectory = unquoted; delete[] unquoted; } if (itksys::SystemTools::MakeDirectory(optionsDirectory.c_str()) == false) { - itkGenericExceptionMacro(<< "Could not create .mitk directory at " << optionsDirectory); + itkGenericExceptionMacro(<< "Could not create .mitk directory at " << Utf8Util::Utf8ToLocal8Bit(optionsDirectory)); } - return optionsDirectory; + return Utf8Util::Utf8ToLocal8Bit(optionsDirectory); } diff --git a/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp b/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp index 8ffbd3bb34..a8a1af5ec1 100644 --- a/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp +++ b/Modules/Core/src/IO/mitkSurfaceVtkIO.cpp @@ -1,97 +1,98 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSurfaceVtkIO.h" #include "mitkSurface.h" +#include #include #include #include #include #include namespace mitk { SurfaceVtkIO::SurfaceVtkIO(const std::string &baseDataType, const CustomMimeType &mimeType, const std::string &description) : AbstractFileIO(baseDataType, mimeType, description) { } vtkSmartPointer SurfaceVtkIO::GetPolyData(unsigned int t, std::string &fileName) { const auto *input = dynamic_cast(this->GetInput()); vtkSmartPointer transformPolyData = vtkSmartPointer::New(); // surfaces do not have to exist in all timesteps; therefor, only write valid surfaces if (input->GetVtkPolyData(t) == nullptr) return vtkSmartPointer(); std::string baseName = this->GetOutputLocation(); - std::string extension = itksys::SystemTools::GetFilenameExtension(baseName); + std::string extension = Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetFilenameExtension(Utf8Util::Local8BitToUtf8(baseName))); if (!extension.empty()) { baseName = baseName.substr(0, baseName.size() - extension.size()); } std::ostringstream ss; ss.imbue(::std::locale::classic()); BaseGeometry *geometry = input->GetGeometry(t); if (input->GetTimeGeometry()->IsValidTimeStep(t)) { if (input->GetTimeGeometry()->CountTimeSteps() > 1) { const TimeBounds &timebounds = input->GetTimeGeometry()->GetTimeBounds(t); ss << baseName << "_S" << std::setprecision(0) << timebounds[0] << "_E" << std::setprecision(0) << timebounds[1] << "_T" << t << extension; } else { // use the original file name ss << this->GetOutputLocation(); } } else { MITK_WARN << "Error on write: TimeGeometry invalid of surface " << fileName << "."; return vtkSmartPointer(); } fileName = ss.str(); transformPolyData->SetInputData(input->GetVtkPolyData(t)); transformPolyData->SetTransform(geometry->GetVtkTransform()); transformPolyData->UpdateWholeExtent(); vtkSmartPointer polyData = transformPolyData->GetOutput(); return polyData; } IFileIO::ConfidenceLevel SurfaceVtkIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; if (this->GetInput()->GetTimeGeometry()->CountTimeSteps() > 1) { // The VTK formats don't support multiple time points. // During writing, we write each time step into a separate file. // For output streams, we only write the first time-step and print a warning. return PartiallySupported; } return Supported; } } diff --git a/Modules/Core/src/IO/mitkUtf8Util.cpp b/Modules/Core/src/IO/mitkUtf8Util.cpp new file mode 100644 index 0000000000..ee1604f144 --- /dev/null +++ b/Modules/Core/src/IO/mitkUtf8Util.cpp @@ -0,0 +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. + +============================================================================*/ + +#include +#include + +#include + +#ifdef US_PLATFORM_WINDOWS + +#include + +namespace +{ + std::wstring MultiByteToWideChar(const std::string& mbString, UINT codePage) + { + auto numChars = ::MultiByteToWideChar(codePage, 0, mbString.data(), mbString.size(), nullptr, 0); + + if (0 >= numChars) + mitkThrow() << "Failure to convert multi-byte character string to wide character string"; + + std::wstring wString; + wString.resize(numChars); + + ::MultiByteToWideChar(codePage, 0, mbString.data(), mbString.size(), &wString[0], static_cast(wString.size())); + + return wString; + } + + std::string WideCharToMultiByte(const std::wstring& wString, UINT codePage) + { + auto numChars = ::WideCharToMultiByte(codePage, 0, wString.data(), wString.size(), nullptr, 0, nullptr, nullptr); + + if (0 >= numChars) + mitkThrow() << "Failure to convert wide character string to multi-byte character string"; + + std::string mbString; + mbString.resize(numChars); + + ::WideCharToMultiByte(codePage, 0, wString.data(), wString.size(), &mbString[0], static_cast(mbString.size()), nullptr, nullptr); + + return mbString; + } +} + +#endif + +std::string mitk::Utf8Util::Local8BitToUtf8(const std::string& local8BitStr) +{ +#ifdef US_PLATFORM_WINDOWS + try + { + return WideCharToMultiByte(MultiByteToWideChar(local8BitStr, CP_ACP), CP_UTF8); + } + catch (const mitk::Exception&) + { + MITK_WARN << "String conversion from current code page to UTF-8 failed. Input string is returned unmodified."; + } +#endif + + return local8BitStr; +} + +std::string mitk::Utf8Util::Utf8ToLocal8Bit(const std::string& utf8Str) +{ +#ifdef US_PLATFORM_WINDOWS + try + { + return WideCharToMultiByte(MultiByteToWideChar(utf8Str, CP_UTF8), CP_ACP); + } + catch (const mitk::Exception&) + { + MITK_WARN << "String conversion from UTF-8 to current code page failed. Input string is returned unmodified."; + } +#endif + + return utf8Str; +} diff --git a/Modules/Core/src/Interactions/mitkDataInteractor.cpp b/Modules/Core/src/Interactions/mitkDataInteractor.cpp index 26fc0e78c1..e80023d1db 100644 --- a/Modules/Core/src/Interactions/mitkDataInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkDataInteractor.cpp @@ -1,98 +1,105 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDataInteractor.h" #include "mitkDataNode.h" #include "mitkStateMachineState.h" +namespace mitk +{ + itkEventMacroDefinition(DataInteractorEvent, itk::AnyEvent); + itkEventMacroDefinition(StartInteraction, DataInteractorEvent); + itkEventMacroDefinition(ResultReady, DataInteractorEvent); +} + // Predefined internal events/signals const std::string mitk::DataInteractor::IntDeactivateMe = "DeactivateMe"; const std::string mitk::DataInteractor::IntLeaveWidget = "LeaveWidget"; const std::string mitk::DataInteractor::IntEnterWidget = "EnterWidget"; mitk::DataInteractor::DataInteractor() { } mitk::DataInteractor::~DataInteractor() { if (!m_DataNode.IsExpired()) { auto dataNode = m_DataNode.Lock(); if (dataNode->GetDataInteractor() == this) dataNode->SetDataInteractor(nullptr); } } mitk::DataNode *mitk::DataInteractor::GetDataNode() const { return m_DataNode.Lock(); } void mitk::DataInteractor::SetDataNode(DataNode *dataNode) { if (dataNode == m_DataNode) return; if (!m_DataNode.IsExpired()) m_DataNode.Lock()->SetDataInteractor(nullptr); m_DataNode = dataNode; if (dataNode != nullptr) m_DataNode.Lock()->SetDataInteractor(this); this->DataNodeChanged(); } int mitk::DataInteractor::GetLayer() const { int layer = -1; if (!m_DataNode.IsExpired()) m_DataNode.Lock()->GetIntProperty("layer", layer); return layer; } void mitk::DataInteractor::ConnectActionsAndFunctions() { MITK_WARN << "DataInteractor::ConnectActionsAndFunctions() is not implemented."; } mitk::ProcessEventMode mitk::DataInteractor::GetMode() const { auto mode = this->GetCurrentState()->GetMode(); if (mode == "PREFER_INPUT") return PREFERINPUT; if (mode == "GRAB_INPUT") return GRABINPUT; return REGULAR; } void mitk::DataInteractor::NotifyStart() { this->GetDataNode()->InvokeEvent(StartInteraction()); } void mitk::DataInteractor::NotifyResultReady() { this->GetDataNode()->InvokeEvent(ResultReady()); } void mitk::DataInteractor::DataNodeChanged() { } diff --git a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp index fb52e35151..5740ea3a58 100644 --- a/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayActionEventBroadcast.cpp @@ -1,914 +1,914 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkDisplayActionEventBroadcast.h" // us #include "usGetModuleContext.h" #include "usModuleContext.h" // mitk core module #include #include #include #include #include #include #include #include #include #include #include #include mitk::DisplayActionEventBroadcast::DisplayActionEventBroadcast() : m_AlwaysReact(false) , m_AutoRepeat(false) , m_IndexToSliceModifier(4) , m_InvertScrollDirection(false) , m_InvertZoomDirection(false) , m_ZoomFactor(2) , m_InvertMoveDirection(false) , m_InvertLevelWindowDirection(false) , m_LinkPlanes(true) { m_StartCoordinateInMM.Fill(0); m_LastDisplayCoordinate.Fill(0); m_LastCoordinateInMM.Fill(0); m_CurrentDisplayCoordinate.Fill(0); // register the broadcast class (itself) as an interaction event observer via micro services us::ServiceProperties props; props["name"] = std::string("DisplayActionEventBroadcast"); m_ServiceRegistration = us::GetModuleContext()->RegisterService(this, props); } mitk::DisplayActionEventBroadcast::~DisplayActionEventBroadcast() { m_ServiceRegistration.Unregister(); } void mitk::DisplayActionEventBroadcast::Notify(InteractionEvent* interactionEvent, bool isHandled) { // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { HandleEvent(interactionEvent, nullptr); } } void mitk::DisplayActionEventBroadcast::ConnectActionsAndFunctions() { CONNECT_CONDITION("check_position_event", CheckPositionEvent); CONNECT_CONDITION("check_can_rotate", CheckRotationPossible); CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible); CONNECT_FUNCTION("init", Init); CONNECT_FUNCTION("move", Move); CONNECT_FUNCTION("zoom", Zoom); CONNECT_FUNCTION("scroll", Scroll); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); CONNECT_FUNCTION("setCrosshair", SetCrosshair); CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar) CONNECT_FUNCTION("startRotation", StartRotation); CONNECT_FUNCTION("endRotation", EndRotation); CONNECT_FUNCTION("rotate", Rotate); CONNECT_FUNCTION("swivel", Swivel); CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep); CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep); } void mitk::DisplayActionEventBroadcast::ConfigurationChanged() { PropertyList::Pointer properties = GetAttributes(); // allwaysReact std::string strAlwaysReact = ""; m_AlwaysReact = false; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } } // auto repeat std::string strAutoRepeat = ""; m_AutoRepeat = false; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; m_IndexToSliceModifier = 4; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } // scroll direction if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection)) { m_ScrollDirection = "updown"; } m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false); // zoom direction if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) { m_ZoomDirection = "updown"; } m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false); m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false); if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection)) { m_LevelDirection = "leftright"; } m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false); // coupled rotation std::string strCoupled = ""; m_LinkPlanes = false; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") { m_LinkPlanes = true; } } // zoom factor std::string strZoomFactor = ""; properties->GetStringProperty("zoomFactor", strZoomFactor); m_ZoomFactor = .05; if (atoi(strZoomFactor.c_str()) > 0) { m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); } } bool mitk::DisplayActionEventBroadcast::FilterEvents(InteractionEvent* interactionEvent, DataNode * /*dataNode*/) { BaseRenderer* sendingRenderer = interactionEvent->GetSender(); if (nullptr == sendingRenderer) { return false; } if (BaseRenderer::Standard3D == sendingRenderer->GetMapperID()) { return false; } return true; } bool mitk::DisplayActionEventBroadcast::CheckPositionEvent(const InteractionEvent *interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } return true; } bool mitk::DisplayActionEventBroadcast::CheckRotationPossible(const InteractionEvent *interactionEvent) { // Decide between moving and rotation slices. /* Detailed logic: 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Needs not even be counted or checked. 2. Inspect every other SliceNavigationController - calculate the line intersection of this SliceNavigationController's plane with our rendering plane - if there is NO intersection, ignore and continue - IF there is an intersection - check the mouse cursor's distance from that line. 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode) 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate - if yes, we just push this line to the "other" lines and rotate it along - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate */ const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } BaseRenderer* renderer = positionEvent->GetSender(); if (nullptr == renderer) { return false; } const PlaneGeometry* rendererWorldPlaneGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (nullptr == rendererWorldPlaneGeometry) { return false; } Point3D position = positionEvent->GetPositionInWorld(); const auto spacing = rendererWorldPlaneGeometry->GetSpacing(); const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY) Line3D intersectionLineWithGeometryToBeRotated; bool hitMultipleLines(false); m_SNCsToBeRotated.clear(); const ScalarType threshholdDistancePixels = 12.0; auto allRenderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { SliceNavigationController* snc = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID()) { continue; } const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry(); if (nullptr == rendererPlaneGeometry) { continue; // ignore, we don't see a plane } // check if there is an intersection between rendered / clicked geometry and the one being analyzed Line3D intersectionLine; if (!rendererWorldPlaneGeometry->IntersectionLine(rendererPlaneGeometry, intersectionLine)) { continue; // we ignore this plane, it's parallel to our plane } // check distance from intersection line const double distanceFromIntersectionLine = intersectionLine.Distance(position) / spacing[snc->GetDefaultViewDirection()]; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { // we just take the last one, so overwrite each iteration (we just need some crossing point) // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used anyOtherGeometry = rendererPlaneGeometry; if (m_LinkPlanes) { // if planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (nullptr == geometryToBeRotated) // first one close to the cursor { geometryToBeRotated = rendererPlaneGeometry; intersectionLineWithGeometryToBeRotated = intersectionLine; m_SNCsToBeRotated.push_back(snc); } else { // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane // together with the primary one // if different, DON'T rotate if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) && intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && rendererWorldPlaneGeometry && !hitMultipleLines) { // assure all three are valid, so calculation of center of rotation can be done moveSlices = false; } // question in state machine is: "rotate?" if (moveSlices) // i.e. NOT rotate { return false; } else { // we have enough information for rotation // remember where the last cursor position ON THE LINE has been observed m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project(position); // find center of rotation by intersection with any of the OTHER lines if (anyOtherGeometry->IntersectionPoint(intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) { return true; } else { return false; } } return false; } bool mitk::DisplayActionEventBroadcast::CheckSwivelPossible(const InteractionEvent *interactionEvent) { // Decide between moving and rotation: if we're close to the crossing // point of the planes, moving mode is entered, otherwise // rotation/swivel mode const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } BaseRenderer* renderer = positionEvent->GetSender(); if (nullptr == renderer) { return false; } const Point3D& position = positionEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry* clickedGeometry(nullptr); const PlaneGeometry* otherGeometry1(nullptr); const PlaneGeometry* otherGeometry2(nullptr); const ScalarType threshholdDistancePixels = 6.0; auto allRenderWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renderWindow : allRenderWindows) { SliceNavigationController* snc = BaseRenderer::GetInstance(renderWindow)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::Standard3D == BaseRenderer::GetInstance(renderWindow)->GetMapperID()) { continue; } const PlaneGeometry* rendererPlaneGeometry = snc->GetCurrentPlaneGeometry(); if (nullptr == rendererPlaneGeometry) { continue; // ignore, we don't see a plane } if (snc == renderer->GetSliceNavigationController()) { clickedGeometry = rendererPlaneGeometry; m_SNCsToBeRotated.push_back(snc); } else { if (nullptr == otherGeometry1) { otherGeometry1 = rendererPlaneGeometry; } else { otherGeometry2 = rendererPlaneGeometry; } if (m_LinkPlanes) { // if planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } Line3D line; Point3D point; if ((nullptr != clickedGeometry) && (nullptr != otherGeometry1) && (nullptr != otherGeometry2) && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) { m_CenterOfRotation = point; if (m_CenterOfRotation.EuclideanDistanceTo(position) < threshholdDistancePixels) { return false; } else { m_ReferenceCursor = positionEvent->GetPointerPositionOnScreen(); // Get main axes of rotation plane and store it for rotation step m_RotationPlaneNormal = clickedGeometry->GetNormal(); ScalarType xVector[] = { 1.0, 0.0, 0.0 }; ScalarType yVector[] = { 0.0, 1.0, 0.0 }; clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector); clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector); m_RotationPlaneNormal.Normalize(); m_RotationPlaneXVector.Normalize(); m_RotationPlaneYVector.Normalize(); m_PreviousRotationAxis.Fill(0.0); m_PreviousRotationAxis[2] = 1.0; m_PreviousRotationAngle = 0.0; return true; } } else { return false; } return false; } void mitk::DisplayActionEventBroadcast::Init(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM); m_LastCoordinateInMM = m_StartCoordinateInMM; } void mitk::DisplayActionEventBroadcast::Move(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } BaseRenderer* sender = interactionEvent->GetSender(); Vector2D moveVector = m_LastDisplayCoordinate - positionEvent->GetPointerPositionOnScreen(); if (m_InvertMoveDirection) { moveVector *= -1.0; } moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); // #TODO: put here? // store new display coordinate m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate move event with computed geometry values InvokeEvent(DisplayMoveEvent(interactionEvent, moveVector)); } void mitk::DisplayActionEventBroadcast::SetCrosshair(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } Point3D position = positionEvent->GetPositionInWorld(); // propagate set crosshair event with computed geometry values InvokeEvent(DisplaySetCrosshairEvent(interactionEvent, position)); } void mitk::DisplayActionEventBroadcast::Zoom(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } float factor = 1.0; float distance = 0; if (m_ZoomDirection == "updown") { distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertZoomDirection) { distance *= -1.0; } // set zooming speed if (distance < 0.0) { factor = 1.0 / m_ZoomFactor; } else if (distance > 0.0) { factor = 1.0 * m_ZoomFactor; } // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate zoom event with computed geometry values InvokeEvent(DisplayZoomEvent(interactionEvent, factor, m_StartCoordinateInMM)); } void mitk::DisplayActionEventBroadcast::Scroll(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } int sliceDelta = 0; // scroll direction if (m_ScrollDirection == "updown") { sliceDelta = static_cast(m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]); } else { sliceDelta = static_cast(m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]); } if (m_InvertScrollDirection) { sliceDelta *= -1; } // set how many pixels the mouse has to be moved to scroll one slice // if the mouse has been moved less than 'm_IndexToSliceModifier', pixels slice ONE slice only if (sliceDelta > 0 && sliceDelta < m_IndexToSliceModifier) { sliceDelta = m_IndexToSliceModifier; } else if (sliceDelta < 0 && sliceDelta > -m_IndexToSliceModifier) { sliceDelta = -m_IndexToSliceModifier; } sliceDelta /= m_IndexToSliceModifier; // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate scroll event with computed geometry values InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::ScrollOneUp(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { int sliceDelta = 1; if (m_InvertScrollDirection) { sliceDelta = -1; } // propagate scroll event with a single slice delta (increase) InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::ScrollOneDown(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { int sliceDelta = -1; if (m_InvertScrollDirection) { sliceDelta = 1; } // propagate scroll event with a single slice delta (decrease) InvokeEvent(DisplayScrollEvent(interactionEvent, sliceDelta, m_AutoRepeat)); } void mitk::DisplayActionEventBroadcast::AdjustLevelWindow(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } ScalarType level; ScalarType window; if (m_LevelDirection == "leftright") { level = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; window = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { level = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; window = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertLevelWindowDirection) { level *= -1; window *= -1; } level *= static_cast(2); window *= static_cast(2); // store new display coordinates m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // propagate set level window event with the level and window delta InvokeEvent(DisplaySetLevelWindowEvent(interactionEvent, level, window)); } void mitk::DisplayActionEventBroadcast::StartRotation(StateMachineAction* /*stateMachineAction*/, InteractionEvent* /*interactionEvent*/) { SetMouseCursor(rotate_cursor_xpm, 0, 0); } void mitk::DisplayActionEventBroadcast::EndRotation(StateMachineAction* /*stateMachineAction*/, InteractionEvent* /*interactionEvent*/) { ResetMouseCursor(); } void mitk::DisplayActionEventBroadcast::Rotate(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } Point3D position = positionEvent->GetPositionInWorld(); Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; Vector3D toCursor = position - m_CenterOfRotation; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector()); - axisOfRotation.SetVnlVector(vnlDirection); + axisOfRotation.SetVnlVector(vnlDirection.as_ref()); // scalar product: A * B = |A| * |B| * cos(angle) // tan = sin / cos ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected)); angle *= 180.0 / vnl_math::pi; m_LastCursorPosition = position; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); // iterate the OTHER slice navigation controllers for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } timeGeometry->ExecuteOperation(&rotationOperation); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayActionEventBroadcast::Swivel(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } // Determine relative mouse movement projected onto world space Point2D position = positionEvent->GetPointerPositionOnScreen(); Vector2D relativeCursor = position - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor movement) Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation operation RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle); SNCVector::iterator iter; for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { TimeGeometry* timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } timeGeometry->ExecuteOperation(&op); (*iter)->SendCreatedWorldGeometryUpdate(); } } // Apply new rotation operation to all relevant SNCs RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle); for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { // Retrieve the TimeGeometry of this SliceNavigationController TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (nullptr == timeGeometry) { continue; } // Execute the new rotation timeGeometry->ExecuteOperation(&op2); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::DisplayActionEventBroadcast::IncreaseTimeStep(StateMachineAction*, InteractionEvent*) { auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Next(); } void mitk::DisplayActionEventBroadcast::DecreaseTimeStep(StateMachineAction*, InteractionEvent*) { auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Previous(); } void mitk::DisplayActionEventBroadcast::UpdateStatusbar(StateMachineAction* /*stateMachineAction*/, InteractionEvent* interactionEvent) { const auto* positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } BaseRenderer::Pointer renderer = positionEvent->GetSender(); TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); DataStorage::SetOfObjects::ConstPointer nodes = renderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); if (nodes.IsNull()) { return; } Point3D worldposition; renderer->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), worldposition); auto globalCurrentTimePoint = renderer->GetTime(); Image::Pointer image3D; DataNode::Pointer node; DataNode::Pointer topSourceNode; int component = 0; node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer); if (node.IsNull()) { return; } bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { DataStorage::SetOfObjects::ConstPointer sourcenodes = renderer->GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, renderer); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } // get the position and pixel value from the image and build up status bar text auto statusBar = StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(worldposition, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); - if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) + if (pixelType == itk::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str()); } - else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR) + else if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR) { std::string pixelValue = "See ODF Details view. "; statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue.c_str()); } else { ScalarType pixelValue; mitkPixelTypeMultiplex5( FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(renderer->GetTimeStep()), p, pixelValue, component); statusBar->DisplayImageInfo(worldposition, p, renderer->GetTime(), pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } bool mitk::DisplayActionEventBroadcast::GetBoolProperty(PropertyList::Pointer propertyList, const char* propertyName, bool defaultValue) { std::string valueAsString; if (!propertyList->GetStringProperty(propertyName, valueAsString)) { return defaultValue; } else { if (valueAsString == "true") { return true; } else { return false; } } } diff --git a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp index bb6a9ae2d8..def8774def 100644 --- a/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkDisplayInteractor.cpp @@ -1,944 +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 "mitkDisplayInteractor.h" #include "mitkBaseRenderer.h" #include "mitkCameraController.h" #include "mitkInteractionPositionEvent.h" #include "mitkPropertyList.h" #include #include #include // level window #include "mitkLevelWindow.h" #include "mitkLevelWindowProperty.h" #include "mitkLine.h" #include "mitkNodePredicateDataType.h" #include "mitkStandaloneDataStorage.h" #include "vtkRenderWindowInteractor.h" // Rotation #include "mitkInteractionConst.h" #include "rotate_cursor.xpm" #include #include #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include "mitkStatusBar.h" #include void mitk::DisplayInteractor::Notify(InteractionEvent *interactionEvent, bool isHandled) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if (!isHandled || m_AlwaysReact) { HandleEvent(interactionEvent, nullptr); } } mitk::DisplayInteractor::DisplayInteractor() : m_IndexToSliceModifier(4) , m_AutoRepeat(false) , m_InvertScrollDirection(false) , m_InvertZoomDirection(false) , m_InvertMoveDirection(false) , m_InvertLevelWindowDirection(false) , m_AlwaysReact(false) , m_ZoomFactor(2) , m_LinkPlanes(true) { m_StartCoordinateInMM.Fill(0); m_LastDisplayCoordinate.Fill(0); m_LastCoordinateInMM.Fill(0); m_CurrentDisplayCoordinate.Fill(0); } mitk::DisplayInteractor::~DisplayInteractor() { // nothing here } void mitk::DisplayInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("check_position_event", CheckPositionEvent); CONNECT_CONDITION("check_can_rotate", CheckRotationPossible); CONNECT_CONDITION("check_can_swivel", CheckSwivelPossible); CONNECT_FUNCTION("init", Init); CONNECT_FUNCTION("move", Move); CONNECT_FUNCTION("zoom", Zoom); CONNECT_FUNCTION("scroll", Scroll); CONNECT_FUNCTION("ScrollOneDown", ScrollOneDown); CONNECT_FUNCTION("ScrollOneUp", ScrollOneUp); CONNECT_FUNCTION("levelWindow", AdjustLevelWindow); CONNECT_FUNCTION("setCrosshair", SetCrosshair); CONNECT_FUNCTION("updateStatusbar", UpdateStatusbar) CONNECT_FUNCTION("startRotation", StartRotation); CONNECT_FUNCTION("endRotation", EndRotation); CONNECT_FUNCTION("rotate", Rotate); CONNECT_FUNCTION("swivel", Swivel); CONNECT_FUNCTION("IncreaseTimeStep", IncreaseTimeStep); CONNECT_FUNCTION("DecreaseTimeStep", DecreaseTimeStep); } bool mitk::DisplayInteractor::CheckPositionEvent(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) { return false; } return true; } bool mitk::DisplayInteractor::CheckRotationPossible(const mitk::InteractionEvent *interactionEvent) { // Decide between moving and rotation slices. /* Detailed logic: 1. Find the SliceNavigationController that has sent the event: this one defines our rendering plane and will NOT be rotated. Needs not even be counted or checked. 2. Inspect every other SliceNavigationController - calculate the line intersection of this SliceNavigationController's plane with our rendering plane - if there is NO interesection, ignore and continue - IF there is an intersection - check the mouse cursor's distance from that line. 0. if the line is NOT near the cursor, remember the plane as "one of the other planes" (which can be rotated in "locked" mode) 1. on first line near the cursor, just remember this intersection line as THE other plane that we want to rotate 2. on every consecutive line near the cursor, check if the line is geometrically identical to the line that we want to rotate - if yes, we just push this line to the "other" lines and rotate it along - if no, then we have a situation where the mouse is near two other lines (e.g. crossing point) and don't want to rotate */ const auto *posEvent = dynamic_cast(interactionEvent); if (posEvent == nullptr) return false; BaseRenderer *clickedRenderer = posEvent->GetSender(); const PlaneGeometry *ourViewportGeometry = (clickedRenderer->GetCurrentWorldPlaneGeometry()); if (!ourViewportGeometry) return false; Point3D cursorPosition = posEvent->GetPositionInWorld(); const auto spacing = ourViewportGeometry->GetSpacing(); const PlaneGeometry *geometryToBeRotated = nullptr; // this one is under the mouse cursor const PlaneGeometry *anyOtherGeometry = nullptr; // this is also visible (for calculation of intersection ONLY) Line3D intersectionLineWithGeometryToBeRotated; bool hitMultipleLines(false); m_SNCsToBeRotated.clear(); const double threshholdDistancePixels = 12.0; auto renWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; const PlaneGeometry *otherRenderersRenderPlane = snc->GetCurrentPlaneGeometry(); if (otherRenderersRenderPlane == nullptr) continue; // ignore, we don't see a plane // check if there is an intersection Line3D intersectionLine; // between rendered/clicked geometry and the one being analyzed if (!ourViewportGeometry->IntersectionLine(otherRenderersRenderPlane, intersectionLine)) { continue; // we ignore this plane, it's parallel to our plane } // check distance from intersection line const double distanceFromIntersectionLine = intersectionLine.Distance(cursorPosition) / spacing[snc->GetDefaultViewDirection()]; // far away line, only remember for linked rotation if necessary if (distanceFromIntersectionLine > threshholdDistancePixels) { anyOtherGeometry = otherRenderersRenderPlane; // we just take the last one, so overwrite each iteration (we just // need some crossing point) // TODO what about multiple crossings? NOW we have undefined behavior / random crossing point is used if (m_LinkPlanes) { m_SNCsToBeRotated.push_back(snc); } } else // close to cursor { if (geometryToBeRotated == nullptr) // first one close to the cursor { geometryToBeRotated = otherRenderersRenderPlane; intersectionLineWithGeometryToBeRotated = intersectionLine; m_SNCsToBeRotated.push_back(snc); } else { // compare to the line defined by geometryToBeRotated: if identical, just rotate this otherRenderersRenderPlane // together with the primary one // if different, DON'T rotate if (intersectionLine.IsParallel(intersectionLineWithGeometryToBeRotated) && intersectionLine.Distance(intersectionLineWithGeometryToBeRotated.GetPoint1()) < mitk::eps) { m_SNCsToBeRotated.push_back(snc); } else { hitMultipleLines = true; } } } } bool moveSlices(true); if (geometryToBeRotated && anyOtherGeometry && ourViewportGeometry && !hitMultipleLines) { // assure all three are valid, so calculation of center of rotation can be done moveSlices = false; } // question in state machine is: "rotate?" if (moveSlices) // i.e. NOT rotate { return false; } else { // we DO have enough information for rotation m_LastCursorPosition = intersectionLineWithGeometryToBeRotated.Project( cursorPosition); // remember where the last cursor position ON THE LINE has been observed if (anyOtherGeometry->IntersectionPoint( intersectionLineWithGeometryToBeRotated, m_CenterOfRotation)) // find center of rotation by intersection with any of the OTHER lines { return true; } else { return false; } } return false; } bool mitk::DisplayInteractor::CheckSwivelPossible(const mitk::InteractionEvent *interactionEvent) { const ScalarType ThresholdDistancePixels = 6.0; // Decide between moving and rotation: if we're close to the crossing // point of the planes, moving mode is entered, otherwise // rotation/swivel mode const auto *posEvent = dynamic_cast(interactionEvent); BaseRenderer *renderer = interactionEvent->GetSender(); if (!posEvent || !renderer) return false; const Point3D &cursor = posEvent->GetPositionInWorld(); m_SNCsToBeRotated.clear(); const PlaneGeometry *clickedGeometry(nullptr); const PlaneGeometry *otherGeometry1(nullptr); const PlaneGeometry *otherGeometry2(nullptr); auto renWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { SliceNavigationController *snc = BaseRenderer::GetInstance(renWin)->GetSliceNavigationController(); // If the mouse cursor is in 3D Renderwindow, do not check for intersecting planes. if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard3D) continue; const PlaneGeometry *planeGeometry = snc->GetCurrentPlaneGeometry(); if (!planeGeometry) continue; if (snc == renderer->GetSliceNavigationController()) { clickedGeometry = planeGeometry; m_SNCsToBeRotated.push_back(snc); } else { if (otherGeometry1 == nullptr) { otherGeometry1 = planeGeometry; } else { otherGeometry2 = planeGeometry; } if (m_LinkPlanes) { // If planes are linked, apply rotation to all planes m_SNCsToBeRotated.push_back(snc); } } } mitk::Line3D line; mitk::Point3D point; if ((clickedGeometry != nullptr) && (otherGeometry1 != nullptr) && (otherGeometry2 != nullptr) && clickedGeometry->IntersectionLine(otherGeometry1, line) && otherGeometry2->IntersectionPoint(line, point)) { m_CenterOfRotation = point; if (m_CenterOfRotation.EuclideanDistanceTo(cursor) < ThresholdDistancePixels) { return false; } else { m_ReferenceCursor = posEvent->GetPointerPositionOnScreen(); // Get main axes of rotation plane and store it for rotation step m_RotationPlaneNormal = clickedGeometry->GetNormal(); ScalarType xVector[] = {1.0, 0.0, 0.0}; ScalarType yVector[] = {0.0, 1.0, 0.0}; clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(xVector), m_RotationPlaneXVector); clickedGeometry->BaseGeometry::IndexToWorld(Vector3D(yVector), m_RotationPlaneYVector); m_RotationPlaneNormal.Normalize(); m_RotationPlaneXVector.Normalize(); m_RotationPlaneYVector.Normalize(); m_PreviousRotationAxis.Fill(0.0); m_PreviousRotationAxis[2] = 1.0; m_PreviousRotationAngle = 0.0; return true; } } else { return false; } return false; } void mitk::DisplayInteractor::Init(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); m_CurrentDisplayCoordinate = m_LastDisplayCoordinate; positionEvent->GetSender()->DisplayToPlane(m_LastDisplayCoordinate, m_StartCoordinateInMM); m_LastCoordinateInMM = m_StartCoordinateInMM; } void mitk::DisplayInteractor::Move(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer *sender = interactionEvent->GetSender(); auto *positionEvent = static_cast(interactionEvent); float invertModifier = -1.0; if (m_InvertMoveDirection) { invertModifier = 1.0; } // perform translation Vector2D moveVector = (positionEvent->GetPointerPositionOnScreen() - m_LastDisplayCoordinate) * invertModifier; moveVector *= sender->GetScaleFactorMMPerDisplayUnit(); sender->GetCameraController()->MoveBy(moveVector); RenderingManager::GetInstance()->RequestUpdate(sender->GetRenderWindow()); m_LastDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); } void mitk::DisplayInteractor::SetCrosshair(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { auto* positionEvent = static_cast(interactionEvent); Point3D pos = positionEvent->GetPositionInWorld(); const BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto renWindows = RenderingManager::GetInstance()->GetAllRegisteredRenderWindows(); for (auto renWin : renWindows) { if (BaseRenderer::GetInstance(renWin)->GetMapperID() == BaseRenderer::Standard2D && renWin != sender->GetRenderWindow()) { BaseRenderer::GetInstance(renWin)->GetSliceNavigationController()->SelectSliceByPoint(pos); } } } void mitk::DisplayInteractor::IncreaseTimeStep(StateMachineAction *, InteractionEvent *) { auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Next(); } void mitk::DisplayInteractor::DecreaseTimeStep(StateMachineAction *, InteractionEvent *) { auto sliceNaviController = RenderingManager::GetInstance()->GetTimeNavigationController(); auto stepper = sliceNaviController->GetTime(); stepper->SetAutoRepeat(true); stepper->Previous(); } void mitk::DisplayInteractor::Zoom(StateMachineAction *, InteractionEvent *interactionEvent) { float factor = 1.0; float distance = 0; if (m_ZoomDirection == "updown") { distance = m_CurrentDisplayCoordinate[1] - m_LastDisplayCoordinate[1]; } else { distance = m_CurrentDisplayCoordinate[0] - m_LastDisplayCoordinate[0]; } if (m_InvertZoomDirection) { distance *= -1.0; } // set zooming speed if (distance < 0.0) { factor = 1.0 / m_ZoomFactor; } else if (distance > 0.0) { factor = 1.0 * m_ZoomFactor; } auto* positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); if (factor != 1.0) { const BaseRenderer::Pointer sender = interactionEvent->GetSender(); sender->GetCameraController()->Zoom(factor, m_StartCoordinateInMM); RenderingManager::GetInstance()->RequestUpdate(sender->GetRenderWindow()); } } void mitk::DisplayInteractor::Scroll(StateMachineAction *, InteractionEvent *interactionEvent) { auto* positionEvent = static_cast(interactionEvent); mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (sliceNaviController) { int delta = 0; // Scrolling direction if (m_ScrollDirection == "updown") { delta = static_cast(m_LastDisplayCoordinate[1] - positionEvent->GetPointerPositionOnScreen()[1]); } else { delta = static_cast(m_LastDisplayCoordinate[0] - positionEvent->GetPointerPositionOnScreen()[0]); } if (m_InvertScrollDirection) { delta *= -1; } // Set how many pixels the mouse has to be moved to scroll one slice // if we moved less than 'm_IndexToSliceModifier' pixels slice ONE slice only if (delta > 0 && delta < m_IndexToSliceModifier) { delta = m_IndexToSliceModifier; } else if (delta < 0 && delta > -m_IndexToSliceModifier) { delta = -m_IndexToSliceModifier; } delta /= m_IndexToSliceModifier; int newPos = sliceNaviController->GetSlice()->GetPos() + delta; // if auto repeat is on, start at first slice if you reach the last slice and vice versa int maxSlices = sliceNaviController->GetSlice()->GetSteps(); if (m_AutoRepeat) { while (newPos < 0) { newPos += maxSlices; } while (newPos >= maxSlices) { newPos -= maxSlices; } } else { // if the new slice is below 0 we still show slice 0 // due to the stepper using unsigned int we have to do this ourselves if (newPos < 1) { newPos = 0; } } m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // set the new position sliceNaviController->GetSlice()->SetPos(newPos); } } void mitk::DisplayInteractor::ScrollOneDown(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Next(); } } void mitk::DisplayInteractor::ScrollOneUp(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::SliceNavigationController::Pointer sliceNaviController = interactionEvent->GetSender()->GetSliceNavigationController(); if (!sliceNaviController->GetSliceLocked()) { mitk::Stepper *stepper = sliceNaviController->GetSlice(); if (stepper->GetSteps() <= 1) { stepper = sliceNaviController->GetTime(); } stepper->Previous(); } } void mitk::DisplayInteractor::AdjustLevelWindow(StateMachineAction *, InteractionEvent *interactionEvent) { BaseRenderer::Pointer sender = interactionEvent->GetSender(); auto *positionEvent = static_cast(interactionEvent); m_LastDisplayCoordinate = m_CurrentDisplayCoordinate; m_CurrentDisplayCoordinate = positionEvent->GetPointerPositionOnScreen(); // search for active image mitk::DataStorage::Pointer storage = sender->GetDataStorage(); mitk::DataNode::Pointer node = nullptr; mitk::DataStorage::SetOfObjects::ConstPointer allImageNodes = storage->GetSubset(mitk::NodePredicateDataType::New("Image")); for (unsigned int i = 0; i < allImageNodes->size(); ++i) { bool isActiveImage = false; bool propFound = allImageNodes->at(i)->GetBoolProperty("imageForLevelWindow", isActiveImage); if (propFound && isActiveImage) { node = allImageNodes->at(i); continue; } } if (node.IsNull()) { node = storage->GetNode(mitk::NodePredicateDataType::New("Image")); } if (node.IsNull()) { return; } mitk::LevelWindow lv = mitk::LevelWindow(); node->GetLevelWindow(lv); ScalarType level = lv.GetLevel(); ScalarType window = lv.GetWindow(); int levelIndex = 0; int windowIndex = 1; if (m_LevelDirection != "leftright") { levelIndex = 1; windowIndex = 0; } int directionModifier = 1; if (m_InvertLevelWindowDirection) { directionModifier = -1; } // calculate adjustments from mouse movements level += (m_CurrentDisplayCoordinate[levelIndex] - m_LastDisplayCoordinate[levelIndex]) * static_cast(2) * directionModifier; window += (m_CurrentDisplayCoordinate[windowIndex] - m_LastDisplayCoordinate[windowIndex]) * static_cast(2) * directionModifier; lv.SetLevelWindow(level, window); dynamic_cast(node->GetProperty("levelwindow"))->SetLevelWindow(lv); RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayInteractor::StartRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->SetMouseCursor(rotate_cursor_xpm, 0, 0); } void mitk::DisplayInteractor::EndRotation(mitk::StateMachineAction *, mitk::InteractionEvent *) { this->ResetMouseCursor(); } void mitk::DisplayInteractor::Rotate(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (posEvent == nullptr) return; Point3D cursor = posEvent->GetPositionInWorld(); Vector3D toProjected = m_LastCursorPosition - m_CenterOfRotation; Vector3D toCursor = cursor - m_CenterOfRotation; // cross product: | A x B | = |A| * |B| * sin(angle) Vector3D axisOfRotation; vnl_vector_fixed vnlDirection = vnl_cross_3d(toCursor.GetVnlVector(), toProjected.GetVnlVector()); - axisOfRotation.SetVnlVector(vnlDirection); + axisOfRotation.SetVnlVector(vnlDirection.as_ref()); // scalar product: A * B = |A| * |B| * cos(angle) // tan = sin / cos ScalarType angle = -atan2((double)(axisOfRotation.GetNorm()), (double)(toCursor * toProjected)); angle *= 180.0 / vnl_math::pi; m_LastCursorPosition = cursor; // create RotationOperation and apply to all SNCs that should be rotated RotationOperation rotationOperation(OpROTATE, m_CenterOfRotation, axisOfRotation, angle); // iterate the OTHER slice navigation controllers: these are filled in DoDecideBetweenRotationAndSliceSelection for (auto iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&rotationOperation); (*iter)->SendCreatedWorldGeometryUpdate(); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::DisplayInteractor::Swivel(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto *posEvent = dynamic_cast(event); if (!posEvent) return; // Determine relative mouse movement projected onto world space Point2D cursor = posEvent->GetPointerPositionOnScreen(); Vector2D relativeCursor = cursor - m_ReferenceCursor; Vector3D relativeCursorAxis = m_RotationPlaneXVector * relativeCursor[0] + m_RotationPlaneYVector * relativeCursor[1]; // Determine rotation axis (perpendicular to rotation plane and cursor // movement) Vector3D rotationAxis = itk::CrossProduct(m_RotationPlaneNormal, relativeCursorAxis); ScalarType rotationAngle = relativeCursor.GetNorm() / 2.0; // Restore the initial plane pose by undoing the previous rotation // operation RotationOperation op(OpROTATE, m_CenterOfRotation, m_PreviousRotationAxis, -m_PreviousRotationAngle); SNCVector::iterator iter; for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; timeGeometry->ExecuteOperation(&op); (*iter)->SendCreatedWorldGeometryUpdate(); } } // Apply new rotation operation to all relevant SNCs RotationOperation op2(OpROTATE, m_CenterOfRotation, rotationAxis, rotationAngle); for (iter = m_SNCsToBeRotated.begin(); iter != m_SNCsToBeRotated.end(); ++iter) { if (!(*iter)->GetSliceRotationLocked()) { // Retrieve the TimeGeometry of this SliceNavigationController TimeGeometry *timeGeometry = (*iter)->GetCreatedWorldGeometry(); if (!timeGeometry) continue; // Execute the new rotation timeGeometry->ExecuteOperation(&op2); // Notify listeners (*iter)->SendCreatedWorldGeometryUpdate(); } } m_PreviousRotationAxis = rotationAxis; m_PreviousRotationAngle = rotationAngle; RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::DisplayInteractor::UpdateStatusbar(mitk::StateMachineAction *, mitk::InteractionEvent *event) { const auto* posEvent = dynamic_cast(event); if (nullptr == posEvent) { return; } const mitk::BaseRenderer::Pointer baseRenderer = posEvent->GetSender(); TNodePredicateDataType::Pointer isImageData = TNodePredicateDataType::New(); auto globalCurrentTimePoint = baseRenderer->GetTime(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = baseRenderer->GetDataStorage()->GetSubset(isImageData).GetPointer(); if (nodes.IsNull()) { return; } // posEvent->GetPositionInWorld() would return the world position at the // time of initiating the interaction. However, we need to update the // status bar with the position after changing slice. Therefore, we // translate the same display position with the renderer again to // get the new world position. Point3D worldposition; baseRenderer->DisplayToWorld(posEvent->GetPointerPositionOnScreen(), worldposition); mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; int component = 0; node = FindTopmostVisibleNode(nodes, worldposition, globalCurrentTimePoint, baseRenderer); if (node.IsNull()) { return; } bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = baseRenderer->GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, worldposition, globalCurrentTimePoint, baseRenderer); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } // get the position and gray value from the image and build up status bar text auto statusBar = StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(worldposition, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); - if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) + if (pixelType == itk::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str()); } - else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR) + else if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR) { std::string pixelValue = "See ODF Details view. "; statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue.c_str()); } else { mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(image3D->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)), p, pixelValue, component); statusBar->DisplayImageInfo(worldposition, p, globalCurrentTimePoint, pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } void mitk::DisplayInteractor::ConfigurationChanged() { mitk::PropertyList::Pointer properties = GetAttributes(); // auto repeat std::string strAutoRepeat = ""; if (properties->GetStringProperty("autoRepeat", strAutoRepeat)) { if (strAutoRepeat == "true") { m_AutoRepeat = true; } else { m_AutoRepeat = false; } } // pixel movement for scrolling one slice std::string strPixelPerSlice = ""; if (properties->GetStringProperty("pixelPerSlice", strPixelPerSlice)) { m_IndexToSliceModifier = atoi(strPixelPerSlice.c_str()); } else { m_IndexToSliceModifier = 4; } // scroll direction if (!properties->GetStringProperty("scrollDirection", m_ScrollDirection)) { m_ScrollDirection = "updown"; } m_InvertScrollDirection = GetBoolProperty(properties, "invertScrollDirection", false); // zoom direction if (!properties->GetStringProperty("zoomDirection", m_ZoomDirection)) { m_ZoomDirection = "updown"; } m_InvertZoomDirection = GetBoolProperty(properties, "invertZoomDirection", false); m_InvertMoveDirection = GetBoolProperty(properties, "invertMoveDirection", false); if (!properties->GetStringProperty("levelWindowDirection", m_LevelDirection)) { m_LevelDirection = "leftright"; } m_InvertLevelWindowDirection = GetBoolProperty(properties, "invertLevelWindowDirection", false); // coupled rotation std::string strCoupled = ""; if (properties->GetStringProperty("coupled", strCoupled)) { if (strCoupled == "true") m_LinkPlanes = true; else m_LinkPlanes = false; } // zoom factor std::string strZoomFactor = ""; properties->GetStringProperty("zoomFactor", strZoomFactor); m_ZoomFactor = .05; if (atoi(strZoomFactor.c_str()) > 0) { m_ZoomFactor = 1.0 + (atoi(strZoomFactor.c_str()) / 100.0); } // allwaysReact std::string strAlwaysReact = ""; if (properties->GetStringProperty("alwaysReact", strAlwaysReact)) { if (strAlwaysReact == "true") { m_AlwaysReact = true; } else { m_AlwaysReact = false; } } else { m_AlwaysReact = false; } } bool mitk::DisplayInteractor::FilterEvents(InteractionEvent *interactionEvent, DataNode * /*dataNode*/) { if (interactionEvent->GetSender() == nullptr) return false; if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D) return false; return true; } bool mitk::DisplayInteractor::GetBoolProperty(mitk::PropertyList::Pointer propertyList, const char *propertyName, bool defaultValue) { std::string valueAsString; if (!propertyList->GetStringProperty(propertyName, valueAsString)) { return defaultValue; } else { if (valueAsString == "true") { return true; } else { return false; } } } diff --git a/Modules/Core/src/Interactions/mitkInteractionSchemeSwitcher.cpp b/Modules/Core/src/Interactions/mitkInteractionSchemeSwitcher.cpp index dc9f1fa6ab..47cc81ee04 100644 --- a/Modules/Core/src/Interactions/mitkInteractionSchemeSwitcher.cpp +++ b/Modules/Core/src/Interactions/mitkInteractionSchemeSwitcher.cpp @@ -1,108 +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 "mitkInteractionSchemeSwitcher.h" // mitk core #include #include +namespace mitk +{ + itkEventMacroDefinition(InteractionSchemeChangedEvent, itk::AnyEvent); +} + mitk::InteractionSchemeSwitcher::InteractionSchemeSwitcher() { // nothing here } mitk::InteractionSchemeSwitcher::~InteractionSchemeSwitcher() { // nothing here } void mitk::InteractionSchemeSwitcher::SetInteractionScheme(InteractionEventHandler* interactionEventHandler, InteractionScheme interactionScheme) { if (nullptr == interactionEventHandler) { mitkThrow() << "Not a valid interaction event handler to set the interaction scheme."; } switch (interactionScheme) { // MITK MODE case MITKStandard: { interactionEventHandler->SetEventConfig("DisplayConfigMITKBase.xml"); interactionEventHandler->AddEventConfig("DisplayConfigCrosshair.xml"); break; } case MITKRotationUncoupled: { interactionEventHandler->SetEventConfig("DisplayConfigMITKBase.xml"); interactionEventHandler->AddEventConfig("DisplayConfigRotation.xml"); break; } case MITKRotationCoupled: { interactionEventHandler->SetEventConfig("DisplayConfigMITKBase.xml"); interactionEventHandler->AddEventConfig("DisplayConfigRotation.xml"); interactionEventHandler->AddEventConfig("DisplayConfigActivateCoupling.xml"); break; } case MITKSwivel: { interactionEventHandler->SetEventConfig("DisplayConfigMITKBase.xml"); interactionEventHandler->AddEventConfig("DisplayConfigSwivel.xml"); break; } // PACS MODE case PACSBase: { interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml"); break; } case PACSStandard: { interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml"); interactionEventHandler->AddEventConfig("DisplayConfigCrosshair.xml"); break; } case PACSLevelWindow: { interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml"); interactionEventHandler->AddEventConfig("DisplayConfigPACSLevelWindow.xml"); break; } case PACSPan: { interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml"); interactionEventHandler->AddEventConfig("DisplayConfigPACSPan.xml"); break; } case PACSScroll: { interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml"); interactionEventHandler->AddEventConfig("DisplayConfigPACSScroll.xml"); break; } case PACSZoom: { interactionEventHandler->SetEventConfig("DisplayConfigPACSBase.xml"); interactionEventHandler->AddEventConfig("DisplayConfigPACSZoom.xml"); break; } default: { interactionEventHandler->SetEventConfig("DisplayConfigMITKBase.xml"); interactionEventHandler->AddEventConfig("DisplayConfigCrosshair.xml"); } } InvokeEvent(InteractionSchemeChangedEvent()); } diff --git a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp index 58f2831295..881e066d0b 100644 --- a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp +++ b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp @@ -1,784 +1,789 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkBaseRenderer.h" #include "mitkMapper.h" #include "mitkResliceMethodProperty.h" // Geometries #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" // Controllers #include "mitkCameraController.h" #include "mitkCameraRotationController.h" #include "mitkSliceNavigationController.h" #include "mitkVtkLayerController.h" #include "mitkInteractionConst.h" #include "mitkProperties.h" #include "mitkWeakPointerProperty.h" // VTK #include #include #include #include #include #include #include +namespace mitk +{ + itkEventMacroDefinition(RendererResetEvent, itk::AnyEvent); +} + mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::baseRendererMap; mitk::BaseRenderer *mitk::BaseRenderer::GetInstance(vtkRenderWindow *renWin) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).first == renWin) return (*mapit).second; } return nullptr; } void mitk::BaseRenderer::AddInstance(vtkRenderWindow *renWin, BaseRenderer *baseRenderer) { if (renWin == nullptr || baseRenderer == nullptr) return; // ensure that no BaseRenderer is managed twice mitk::BaseRenderer::RemoveInstance(renWin); baseRendererMap.insert(BaseRendererMapType::value_type(renWin, baseRenderer)); } void mitk::BaseRenderer::RemoveInstance(vtkRenderWindow *renWin) { auto mapit = baseRendererMap.find(renWin); if (mapit != baseRendererMap.end()) baseRendererMap.erase(mapit); } mitk::BaseRenderer *mitk::BaseRenderer::GetByName(const std::string &name) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).second; } return nullptr; } vtkRenderWindow *mitk::BaseRenderer::GetRenderWindowByName(const std::string &name) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).first; } return nullptr; } mitk::BaseRenderer::BaseRenderer(const char *name, vtkRenderWindow *renWin) : m_RenderWindow(nullptr), m_VtkRenderer(nullptr), m_MapperID(defaultMapper), m_DataStorage(nullptr), m_LastUpdateTime(0), m_CameraController(nullptr), m_SliceNavigationController(nullptr), m_CameraRotationController(nullptr), m_WorldTimeGeometry(nullptr), m_CurrentWorldGeometry(nullptr), m_CurrentWorldPlaneGeometry(nullptr), m_Slice(0), m_TimeStep(), m_CurrentWorldPlaneGeometryUpdateTime(), m_TimeStepUpdateTime(), m_KeepDisplayedRegion(true), m_CurrentWorldPlaneGeometryData(nullptr), m_CurrentWorldPlaneGeometryNode(nullptr), m_CurrentWorldPlaneGeometryTransformTime(0), m_Name(name), m_EmptyWorldGeometry(true), m_NumberOfVisibleLODEnabledMappers(0) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; if (name != nullptr) { m_Name = name; } else { m_Name = "unnamed renderer"; itkWarningMacro(<< "Created unnamed renderer. Bad for serialization. Please choose a name."); } if (renWin != nullptr) { m_RenderWindow = renWin; m_RenderWindow->Register(nullptr); } else { itkWarningMacro(<< "Created mitkBaseRenderer without vtkRenderWindow present."); } // instances.insert( this ); // adding this BaseRenderer to the List of all BaseRenderer m_BindDispatcherInteractor = new mitk::BindDispatcherInteractor(GetName()); WeakPointerProperty::Pointer rendererProp = WeakPointerProperty::New((itk::Object *)this); m_CurrentWorldPlaneGeometry = mitk::PlaneGeometry::New(); m_CurrentWorldPlaneGeometryData = mitk::PlaneGeometryData::New(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryNode = mitk::DataNode::New(); m_CurrentWorldPlaneGeometryNode->SetData(m_CurrentWorldPlaneGeometryData); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("layer", IntProperty::New(1000)); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(1)); m_CurrentWorldPlaneGeometryTransformTime = m_CurrentWorldPlaneGeometryNode->GetVtkTransform()->GetMTime(); mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::SliceNavigationController::New(); sliceNavigationController->SetRenderer(this); sliceNavigationController->ConnectGeometrySliceEvent(this); sliceNavigationController->ConnectGeometryUpdateEvent(this); sliceNavigationController->ConnectGeometryTimeEvent(this, false); m_SliceNavigationController = sliceNavigationController; m_CameraRotationController = mitk::CameraRotationController::New(); m_CameraRotationController->SetRenderWindow(m_RenderWindow); m_CameraRotationController->AcquireCamera(); m_CameraController = mitk::CameraController::New(); m_CameraController->SetRenderer(this); m_VtkRenderer = vtkRenderer::New(); m_VtkRenderer->SetMaximumNumberOfPeels(16); if (AntiAliasing::FastApproximate == RenderingManager::GetInstance()->GetAntiAliasing()) m_VtkRenderer->UseFXAAOn(); if (nullptr == mitk::VtkLayerController::GetInstance(m_RenderWindow)) mitk::VtkLayerController::AddInstance(m_RenderWindow, m_VtkRenderer); mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } mitk::BaseRenderer::~BaseRenderer() { if (m_VtkRenderer != nullptr) { m_VtkRenderer->Delete(); m_VtkRenderer = nullptr; } if (m_CameraController.IsNotNull()) m_CameraController->SetRenderer(nullptr); mitk::VtkLayerController::RemoveInstance(m_RenderWindow); RemoveAllLocalStorages(); m_DataStorage = nullptr; if (m_BindDispatcherInteractor != nullptr) { delete m_BindDispatcherInteractor; } if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); m_RenderWindow = nullptr; } } void mitk::BaseRenderer::SetMapperID(MapperSlotId id) { if (m_MapperID != id) { bool useDepthPeeling = Standard3D == id; m_VtkRenderer->SetUseDepthPeeling(useDepthPeeling); m_VtkRenderer->SetUseDepthPeelingForVolumes(useDepthPeeling); m_MapperID = id; this->Modified(); } } void mitk::BaseRenderer::RemoveAllLocalStorages() { - this->InvokeEvent(mitk::BaseRenderer::RendererResetEvent()); + this->InvokeEvent(RendererResetEvent()); std::list::iterator it; for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); ++it) (*it)->ClearLocalStorage(this, false); m_RegisteredLocalStorageHandlers.clear(); } void mitk::BaseRenderer::RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.push_back(lsh); } mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() const { return m_BindDispatcherInteractor->GetDispatcher(); } void mitk::BaseRenderer::UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.remove(lsh); } void mitk::BaseRenderer::SetDataStorage(DataStorage *storage) { if (storage != m_DataStorage && storage != nullptr) { m_DataStorage = storage; m_BindDispatcherInteractor->SetDataStorage(m_DataStorage); this->Modified(); } } const mitk::BaseRenderer::MapperSlotId mitk::BaseRenderer::defaultMapper = 1; void mitk::BaseRenderer::Paint() { } void mitk::BaseRenderer::Initialize() { } void mitk::BaseRenderer::Resize(int w, int h) { this->m_RenderWindow->SetSize(w, h); } void mitk::BaseRenderer::InitRenderer(vtkRenderWindow *renderwindow) { if (m_RenderWindow != renderwindow) { if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); } m_RenderWindow = renderwindow; if (m_RenderWindow != nullptr) { m_RenderWindow->Register(nullptr); } } RemoveAllLocalStorages(); if (m_CameraController.IsNotNull()) { m_CameraController->SetRenderer(this); } } void mitk::BaseRenderer::InitSize(int w, int h) { this->m_RenderWindow->SetSize(w, h); } void mitk::BaseRenderer::SetSlice(unsigned int slice) { if (m_Slice != slice) { m_Slice = slice; if (m_WorldTimeGeometry.IsNotNull()) { // get world geometry which may be rotated, for the current time step SlicedGeometry3D *slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != nullptr) { // if slice position is part of the world geometry... if (m_Slice >= slicedWorldGeometry->GetSlices()) // set the current worldplanegeomety as the selected 2D slice of the world geometry m_Slice = slicedWorldGeometry->GetSlices() - 1; SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } void mitk::BaseRenderer::SetTimeStep(unsigned int timeStep) { if (m_TimeStep != timeStep) { m_TimeStep = timeStep; m_TimeStepUpdateTime.Modified(); if (m_WorldTimeGeometry.IsNotNull()) { if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; SlicedGeometry3D *slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != nullptr) { SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } mitk::TimeStepType mitk::BaseRenderer::GetTimeStep(const mitk::BaseData *data) const { if ((data == nullptr) || (data->IsInitialized() == false)) { return -1; } return data->GetTimeGeometry()->TimePointToTimeStep(GetTime()); } mitk::ScalarType mitk::BaseRenderer::GetTime() const { if (m_WorldTimeGeometry.IsNull()) { return 0; } else { ScalarType timeInMS = m_WorldTimeGeometry->TimeStepToTimePoint(GetTimeStep()); if (timeInMS == itk::NumericTraits::NonpositiveMin()) return 0; else return timeInMS; } } void mitk::BaseRenderer::SetWorldTimeGeometry(const mitk::TimeGeometry *geometry) { assert(geometry != nullptr); itkDebugMacro("setting WorldTimeGeometry to " << geometry); if (m_WorldTimeGeometry != geometry) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() == 0) return; m_WorldTimeGeometry = geometry; itkDebugMacro("setting WorldTimeGeometry to " << m_WorldTimeGeometry); if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; BaseGeometry *geometry3d; geometry3d = m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep); SetWorldGeometry3D(geometry3d); } } void mitk::BaseRenderer::SetWorldGeometry3D(const mitk::BaseGeometry *geometry) { itkDebugMacro("setting WorldGeometry3D to " << geometry); if (geometry->GetBoundingBox()->GetDiagonalLength2() == 0) return; const SlicedGeometry3D *slicedWorldGeometry; slicedWorldGeometry = dynamic_cast(geometry); PlaneGeometry::ConstPointer geometry2d; if (slicedWorldGeometry != nullptr) { if (m_Slice >= slicedWorldGeometry->GetSlices() && (m_Slice != 0)) m_Slice = slicedWorldGeometry->GetSlices() - 1; geometry2d = slicedWorldGeometry->GetPlaneGeometry(m_Slice); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(slicedWorldGeometry); geometry2d = plane; } SetCurrentWorldGeometry(slicedWorldGeometry); } else { geometry2d = dynamic_cast(geometry); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = PlaneGeometry::New(); plane->InitializeStandardPlane(geometry); geometry2d = plane; } SetCurrentWorldGeometry(geometry); } SetCurrentWorldPlaneGeometry(geometry2d); // calls Modified() if (m_CurrentWorldPlaneGeometry.IsNull()) itkWarningMacro("m_CurrentWorldPlaneGeometry is nullptr"); } void mitk::BaseRenderer::SetCurrentWorldPlaneGeometry(const mitk::PlaneGeometry *geometry2d) { if (m_CurrentWorldPlaneGeometry != geometry2d) { m_CurrentWorldPlaneGeometry = geometry2d->Clone(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryUpdateTime.Modified(); Modified(); } } void mitk::BaseRenderer::SendUpdateSlice() { m_CurrentWorldPlaneGeometryUpdateTime.Modified(); } int *mitk::BaseRenderer::GetSize() const { return this->m_RenderWindow->GetSize(); } int *mitk::BaseRenderer::GetViewportSize() const { return this->m_VtkRenderer->GetSize(); } void mitk::BaseRenderer::SetCurrentWorldGeometry(const mitk::BaseGeometry *geometry) { m_CurrentWorldGeometry = geometry; if (geometry == nullptr) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; m_EmptyWorldGeometry = true; return; } BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(nullptr); const BoundingBox::BoundsArrayType &worldBounds = boundingBox->GetBounds(); m_Bounds[0] = worldBounds[0]; m_Bounds[1] = worldBounds[1]; m_Bounds[2] = worldBounds[2]; m_Bounds[3] = worldBounds[3]; m_Bounds[4] = worldBounds[4]; m_Bounds[5] = worldBounds[5]; if (boundingBox->GetDiagonalLength2() <= mitk::eps) m_EmptyWorldGeometry = true; else m_EmptyWorldGeometry = false; } void mitk::BaseRenderer::SetGeometry(const itk::EventObject &geometrySendEvent) { const auto *sendEvent = dynamic_cast(&geometrySendEvent); assert(sendEvent != nullptr); SetWorldTimeGeometry(sendEvent->GetTimeGeometry()); } void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject &geometryUpdateEvent) { const auto *updateEvent = dynamic_cast(&geometryUpdateEvent); if (updateEvent == nullptr) return; if (m_CurrentWorldGeometry.IsNotNull()) { auto *slicedWorldGeometry = dynamic_cast(m_CurrentWorldGeometry.GetPointer()); if (slicedWorldGeometry) { PlaneGeometry *geometry2D = slicedWorldGeometry->GetPlaneGeometry(m_Slice); SetCurrentWorldPlaneGeometry(geometry2D); // calls Modified() } } } void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject &geometrySliceEvent) { const auto *sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent != nullptr); SetSlice(sliceEvent->GetPos()); } void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject &geometryTimeEvent) { const auto *timeEvent = dynamic_cast(&geometryTimeEvent); assert(timeEvent != nullptr); SetTimeStep(timeEvent->GetPos()); } const double *mitk::BaseRenderer::GetBounds() const { return m_Bounds; } void mitk::BaseRenderer::DrawOverlayMouse(mitk::Point2D &itkNotUsed(p2d)) { MITK_INFO << "BaseRenderer::DrawOverlayMouse()- should be inconcret implementation OpenGLRenderer." << std::endl; } void mitk::BaseRenderer::RequestUpdate() { SetConstrainZoomingAndPanning(true); RenderingManager::GetInstance()->RequestUpdate(this->m_RenderWindow); } void mitk::BaseRenderer::ForceImmediateUpdate() { RenderingManager::GetInstance()->ForceImmediateUpdate(this->m_RenderWindow); } unsigned int mitk::BaseRenderer::GetNumberOfVisibleLODEnabledMappers() const { return m_NumberOfVisibleLODEnabledMappers; } /*! Sets the new Navigation controller */ void mitk::BaseRenderer::SetSliceNavigationController(mitk::SliceNavigationController *SlicenavigationController) { if (SlicenavigationController == nullptr) return; // copy worldgeometry SlicenavigationController->SetInputWorldTimeGeometry(SlicenavigationController->GetCreatedWorldGeometry()); SlicenavigationController->Update(); // set new m_SliceNavigationController = SlicenavigationController; m_SliceNavigationController->SetRenderer(this); if (m_SliceNavigationController.IsNotNull()) { m_SliceNavigationController->ConnectGeometrySliceEvent(this); m_SliceNavigationController->ConnectGeometryUpdateEvent(this); m_SliceNavigationController->ConnectGeometryTimeEvent(this, false); } } void mitk::BaseRenderer::DisplayToWorld(const Point2D &displayPoint, Point3D &worldIndex) const { if (m_MapperID == BaseRenderer::Standard2D) { double display[3], *world; // For the rigth z-position in display coordinates, take the focal point, convert it to display and use it for // correct depth. double *displayCoord; double cameraFP[4]; // Get camera focal point and position. Convert to display (screen) // coordinates. We need a depth value for z-buffer. this->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint(cameraFP); cameraFP[3] = 0.0; this->GetVtkRenderer()->SetWorldPoint(cameraFP[0], cameraFP[1], cameraFP[2], cameraFP[3]); this->GetVtkRenderer()->WorldToDisplay(); displayCoord = this->GetVtkRenderer()->GetDisplayPoint(); // now convert the display point to world coordinates display[0] = displayPoint[0]; display[1] = displayPoint[1]; display[2] = displayCoord[2]; this->GetVtkRenderer()->SetDisplayPoint(display); this->GetVtkRenderer()->DisplayToWorld(); world = this->GetVtkRenderer()->GetWorldPoint(); for (int i = 0; i < 3; i++) { worldIndex[i] = world[i] / world[3]; } } else if (m_MapperID == BaseRenderer::Standard3D) { PickWorldPoint( displayPoint, worldIndex); // Seems to be the same code as above, but subclasses may contain different implementations. } return; } void mitk::BaseRenderer::DisplayToPlane(const Point2D &displayPoint, Point2D &planePointInMM) const { if (m_MapperID == BaseRenderer::Standard2D) { Point3D worldPoint; this->DisplayToWorld(displayPoint, worldPoint); this->m_CurrentWorldPlaneGeometry->Map(worldPoint, planePointInMM); } else if (m_MapperID == BaseRenderer::Standard3D) { MITK_WARN << "No conversion possible with 3D mapper."; return; } return; } void mitk::BaseRenderer::WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const { double world[4], *display; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToDisplay(); display = this->GetVtkRenderer()->GetDisplayPoint(); displayPoint[0] = display[0]; displayPoint[1] = display[1]; return; } void mitk::BaseRenderer::WorldToView(const mitk::Point3D &worldIndex, mitk::Point2D &viewPoint) const { double world[4], *view; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToView(); view = this->GetVtkRenderer()->GetViewPoint(); this->GetVtkRenderer()->ViewToNormalizedViewport(view[0], view[1], view[2]); viewPoint[0] = view[0] * this->GetViewportSize()[0]; viewPoint[1] = view[1] * this->GetViewportSize()[1]; return; } void mitk::BaseRenderer::PlaneToDisplay(const Point2D &planePointInMM, Point2D &displayPoint) const { Point3D worldPoint; this->m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToDisplay(worldPoint, displayPoint); return; } void mitk::BaseRenderer::PlaneToView(const Point2D &planePointInMM, Point2D &viewPoint) const { Point3D worldPoint; this->m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToView(worldPoint,viewPoint); return; } double mitk::BaseRenderer::GetScaleFactorMMPerDisplayUnit() const { if (this->GetMapperID() == BaseRenderer::Standard2D) { // GetParallelScale returns half of the height of the render window in mm. // Divided by the half size of the Display size in pixel givest the mm per pixel. return this->GetVtkRenderer()->GetActiveCamera()->GetParallelScale() * 2.0 / GetViewportSize()[1]; } else return 1.0; } mitk::Point2D mitk::BaseRenderer::GetDisplaySizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetSizeX() * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetSizeY() * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetViewportSizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetViewportSize()[0] * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetViewportSize()[1] * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetOriginInMM() const { Point2D originPx; originPx[0] = m_VtkRenderer->GetOrigin()[0]; originPx[1] = m_VtkRenderer->GetOrigin()[1]; Point2D displayGeometryOriginInMM; DisplayToPlane(originPx, displayGeometryOriginInMM); // top left of the render window (Origin) return displayGeometryOriginInMM; } void mitk::BaseRenderer::SetConstrainZoomingAndPanning(bool constrain) { m_ConstrainZoomingAndPanning = constrain; if (m_ConstrainZoomingAndPanning) { this->GetCameraController()->AdjustCameraToPlane(); } } mitk::Point3D mitk::BaseRenderer::Map2DRendererPositionTo3DWorldPosition(const Point2D &mousePosition) const { // DEPRECATED: Map2DRendererPositionTo3DWorldPosition is deprecated. use DisplayToWorldInstead Point3D position; DisplayToWorld(mousePosition, position); return position; } void mitk::BaseRenderer::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " MapperID: " << m_MapperID << std::endl; os << indent << " Slice: " << m_Slice << std::endl; os << indent << " TimeStep: " << m_TimeStep << std::endl; os << indent << " CurrentWorldPlaneGeometry: "; if (m_CurrentWorldPlaneGeometry.IsNull()) os << "nullptr" << std::endl; else m_CurrentWorldPlaneGeometry->Print(os, indent); os << indent << " CurrentWorldPlaneGeometryUpdateTime: " << m_CurrentWorldPlaneGeometryUpdateTime << std::endl; os << indent << " CurrentWorldPlaneGeometryTransformTime: " << m_CurrentWorldPlaneGeometryTransformTime << std::endl; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp index 5e4e797c38..12555a0d66 100644 --- a/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp +++ b/Modules/Core/src/Rendering/mitkImageVtkMapper2D.cpp @@ -1,1153 +1,1153 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 #include #include #include #include #include #include #include #include #include #include //#include #include "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include // MITK Rendering #include "mitkImageVtkMapper2D.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkNeverTranslucentTexture.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include #include namespace { bool IsBinaryImage(mitk::Image* image) { if (nullptr != image && image->IsInitialized()) { bool isBinary = true; auto statistics = image->GetStatistics(); const auto numTimeSteps = image->GetTimeSteps(); for (std::remove_const_t t = 0; t < numTimeSteps; ++t) { const auto numChannels = image->GetNumberOfChannels(); for (std::remove_const_t c = 0; c < numChannels; ++c) { auto minValue = statistics->GetScalarValueMin(t, c); auto maxValue = statistics->GetScalarValueMax(t, c); if (std::abs(maxValue - minValue) < mitk::eps) continue; auto min2ndValue = statistics->GetScalarValue2ndMin(t, c); auto max2ndValue = statistics->GetScalarValue2ndMax(t, c); if (std::abs(maxValue - min2ndValue) < mitk::eps && std::abs(max2ndValue - minValue) < mitk::eps) continue; isBinary = false; break; } if (!isBinary) break; } return isBinary; } return false; } } mitk::ImageVtkMapper2D::ImageVtkMapper2D() { } mitk::ImageVtkMapper2D::~ImageVtkMapper2D() { // The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event, // in order to delete the images from the 3D RW. this->InvokeEvent(itk::DeleteEvent()); } // set the two points defining the textured plane according to the dimension and spacing void mitk::ImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); // Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct // plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); // These two points define the axes of the plane in combination with the origin. // Point 1 is the x-axis and point 2 the y-axis. // Each plane is transformed according to the view (axial, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth) } float mitk::ImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer) { // get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; // Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange * 0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty("layer", layer, renderer); // add the layer property for each image to render images with a higher layer on top of the others depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between) if (depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image *mitk::ImageVtkMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::ImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_PublicActors; } void mitk::ImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); auto *image = const_cast(this->GetInput()); mitk::DataNode *datanode = this->GetDataNode(); if (nullptr == image || !image->IsInitialized()) { this->SetToInvalidState(localStorage); return; } // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (nullptr == worldGeometry || !worldGeometry->IsValid() || !worldGeometry->HasReferenceGeometry()) { this->SetToInvalidState(localStorage); return; } image->Update(); localStorage->m_PublicActors = localStorage->m_Actors.Get(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if (!RenderingGeometryIntersectsImage(worldGeometry, image->GetSlicedGeometry())) { this->SetToInvalidState(localStorage); return; } // set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(image); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())); // is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ((image->GetDimension() >= 3) && (image->GetDimension(2) > 1)) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation", renderer); int interpolationMode = VTK_RESLICE_NEAREST; if (resliceInterpolationProperty != nullptr) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch (interpolationMode) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } // set the vtk output property to true, makes sure that no unneeded mitk image convertion // is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); // Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if (image->GetPixelType().GetNumberOfComponents() == 1) // for now only single component are allowed { DataNode *dn = renderer->GetCurrentWorldPlaneGeometryNode(); if (dn) { ResliceMethodProperty *resliceMethodEnumProperty = nullptr; if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices", renderer) && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num", renderer) && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 1; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const auto *planeGeometry = dynamic_cast(worldGeometry); if (thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; const auto *abstractGeometry = dynamic_cast(worldGeometry); if (abstractGeometry != nullptr) normal = abstractGeometry->GetPlane()->GetNormal(); else { if (planeGeometry != nullptr) { normal = planeGeometry->GetNormal(); } else return; // no fitting geometry set } normal.Normalize(); image->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())->WorldToIndex(normal, normInIndex); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality(3); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection(-thickSlicesNum, 0 + thickSlicesNum); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode(thickSlicesMode - 1); localStorage->m_TSFilter->SetInputData(localStorage->m_Reslicer->GetVtkOutput()); // vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { // this is needed when thick mode was enable bevore. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality(2); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection(0, 0); localStorage->m_Reslicer->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) // this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; for (auto &sliceBound : sliceBounds) { sliceBound = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { double textureClippingBounds[6]; for (auto &textureClippingBound : textureClippingBounds) { textureClippingBound = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds(image->GetGeometry(), planeGeometry, textureClippingBounds); textureClippingBounds[0] = static_cast(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[1] = static_cast(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[2] = static_cast(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5); textureClippingBounds[3] = static_cast(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5); // clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } // get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); // get the binary property bool binary = false; bool binaryOutline = false; datanode->GetBoolProperty("binary", binary, renderer); if (binary) // binary image { datanode->GetBoolProperty("outline binary", binaryOutline, renderer); if (binaryOutline) // contour rendering { // get pixel type of vtk image - itk::ImageIOBase::IOComponentType componentType = static_cast(image->GetPixelType().GetComponentType()); + auto componentType = image->GetPixelType().GetComponentType(); switch (componentType) { - case itk::ImageIOBase::UCHAR: + case itk::IOComponentEnum::UCHAR: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; - case itk::ImageIOBase::USHORT: + case itk::IOComponentEnum::USHORT: // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); break; default: binaryOutline = false; this->ApplyLookuptable(renderer); MITK_WARN << "Type of all binary images should be unsigned char or unsigned short. Outline does not work on other pixel types!"; } if (binaryOutline) // binary outline is still true --> add outline { float binaryOutlineWidth = 1.0; if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer)) { float binaryOutlineShadowWidth = 1.5; datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer); localStorage->m_ShadowOutlineActor->GetProperty()->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth); localStorage->m_ImageActor->GetProperty()->SetLineWidth(binaryOutlineWidth); } } } else // standard binary image { if (numberOfComponents != 1) { MITK_ERROR << "Rendering Error: Binary Images with more then 1 component are not supported!"; } } } this->ApplyOpacity(renderer); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->SetColorModeToDirectScalars(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { // connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty("texture interpolation", textureInterpolation, renderer); // set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); // connect the texture with the output of the levelwindow filter localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); this->TransformActor(renderer); if (binary && binaryOutline) // connect the mapper with the polyData which contains the lines { // We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); localStorage->m_ImageActor->SetTexture(nullptr); // no texture for contours bool binaryOutlineShadow = false; datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer); if (binaryOutlineShadow) { localStorage->m_ShadowOutlineActor->SetVisibility(true); } else { localStorage->m_ShadowOutlineActor->SetVisibility(false); } } else { // Connect the mapper with the input texture. This is the standard case. // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_ImageActor->SetTexture(localStorage->m_Texture); localStorage->m_ShadowOutlineActor->SetVisibility(false); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelwindow"); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); mitk::LevelWindow opacLevelWindow; if (this->GetDataNode()->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow")) { // pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { // no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::ImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float rgb[3] = {1.0f, 1.0f, 1.0f}; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; bool binary = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary && hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (binary && selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (!binary || (!hover && !selected)) { GetDataNode()->GetColor(rgb, renderer, "color"); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK localStorage->m_ShadowOutlineActor->GetProperty()->SetColor(rgbConv); localStorage->m_ImageActor->GetProperty()->SetColor(rgbConv); float shadowRGB[3] = {1.0f, 1.0f, 1.0f}; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("outline binary shadow color", renderer)); if (colorprop.IsNotNull()) { memcpy(shadowRGB, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } double shadowRGBConv[3] = {(double)shadowRGB[0], (double)shadowRGB[1], (double)shadowRGB[2]}; // conversion to double for VTK localStorage->m_ShadowOutlineActor->GetProperty()->SetColor(shadowRGBConv); } void mitk::ImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(opacity, renderer, "opacity"); // set the opacity according to the properties localStorage->m_ImageActor->GetProperty()->SetOpacity(opacity); localStorage->m_ShadowOutlineActor->GetProperty()->SetOpacity(opacity); } void mitk::ImageVtkMapper2D::ApplyRenderingMode(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary) // is it a binary image? { // for binary images, we always use our default LuT and map every value to (0,1) // the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { // all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Mode", renderer)); if (mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch (renderingMode) { case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead."; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; } } // we apply color for all images (including binaries). this->ApplyColor(renderer); } void mitk::ImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable *usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable", renderer)); if (lookupTableProp.IsNotNull()) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { //"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. // A default (rainbow) lookup table will be used. // Here have to do nothing. Warning for the user has been removed, due to unwanted console output // in every interation of the rendering. } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::ImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast( this->GetDataNode()->GetProperty("Image Rendering.Transfer Function", renderer)); if (transferFunctionProp.IsNull()) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image " "Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); localStorage->m_LevelWindowFilter->SetOpacityPiecewiseFunction( transferFunctionProp->GetValue()->GetScalarOpacityFunction()); } void mitk::ImageVtkMapper2D::SetToInvalidState(mitk::ImageVtkMapper2D::LocalStorage* localStorage) { localStorage->m_PublicActors = localStorage->m_EmptyActors.Get(); // set image to nullptr, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = nullptr; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); } void mitk::ImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } auto *data = const_cast(this->GetInput()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { this->SetToInvalidState(localStorage); return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime()) || (localStorage->m_LastUpdateTime < data->GetPropertyList()->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::ImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty("depthOffset", mitk::FloatProperty::New(0.0), renderer, overwrite); node->AddProperty("outline binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline width", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("outline binary shadow", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline binary shadow color", ColorProperty::New(0.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("outline shadow width", mitk::FloatProperty::New(1.5), renderer, overwrite); if (image->IsRotated()) node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC)); else node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New()); node->AddProperty("texture interpolation", mitk::BoolProperty::New(false)); node->AddProperty("in plane resample extent by geometry", mitk::BoolProperty::New(false)); node->AddProperty("bounding box", mitk::BoolProperty::New(false)); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty("Image Rendering.Mode", renderingModeProperty); // Set default grayscale look-up table mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); mitkLut->SetType(mitk::LookupTable::GRAYSCALE); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp, renderer); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if (node->GetStringProperty("dicom.pixel.PhotometricInterpretation", photometricInterpretation)) { // modality provided by DICOM or other reader if (photometricInterpretation.find("MONOCHROME1") != std::string::npos) // meaning: display MINIMUM pixels as WHITE { // Set inverse grayscale look-up table mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp, renderer); renderingModeProperty->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); // USE lookuptable } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if (!node->GetBoolProperty("binary", isBinaryImage) && image->GetPixelType().GetNumberOfComponents() == 1) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); isBinaryImage = IsBinaryImage(centralSliceImage); if (isBinaryImage) // Potential binary image. Now take a close look. isBinaryImage = IsBinaryImage(image); } std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "OdfImage" && className != "ShImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); - if ((pixelType.GetPixelType() == itk::ImageIOBase::VECTOR && numComponents > 1) || numComponents == 2 || + if ((pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR && numComponents > 1) || numComponents == 2 || numComponents > 4) { node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty("opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 1.0, 1.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); } if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { /* initialize level/window from DICOM tags */ mitk::LevelWindow contrast; std::string sLevel = ""; std::string sWindow = ""; if (GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1050, "dicom.voilut.WindowCenter", image->GetPropertyList(), sLevel) && GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1051, "dicom.voilut.WindowWidth", image->GetPropertyList(), sWindow)) { float level = atof(sLevel.c_str()); float window = atof(sWindow.c_str()); std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if (GetBackwardsCompatibleDICOMProperty(0x0028, 0x0108, "dicom.series.SmallestPixelValueInSeries", image->GetPropertyList(), sSmallestPixelValueInSeries) && GetBackwardsCompatibleDICOMProperty(0x0028, 0x0109, "dicom.series.LargestPixelValueInSeries", image->GetPropertyList(), sLargestPixelValueInSeries)) { float smallestPixelValueInSeries = atof(sSmallestPixelValueInSeries.c_str()); float largestPixelValueInSeries = atof(sLargestPixelValueInSeries.c_str()); contrast.SetRangeMinMax(smallestPixelValueInSeries - 1, largestPixelValueInSeries + 1); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // fallback } contrast.SetLevelWindow(level, window, true); } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // fallback } node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer); } if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) && - (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) && - (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR)) + (image->GetPixelType().GetPixelType() == itk::IOPixelEnum::RGBA) && + (image->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR)) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0, 255); opaclevwin.SetWindowBounds(0, 255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty("opaclevelwindow", prop, renderer); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::ImageVtkMapper2D::LocalStorage *mitk::ImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } const mitk::ImageVtkMapper2D::LocalStorage* mitk::ImageVtkMapper2D::GetConstLocalStorage(mitk::BaseRenderer* renderer) { return m_LSH.GetLocalStorage(renderer); } template vtkSmartPointer mitk::ImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); // get the min and max index values of each direction int *extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int *dims = localStorage->m_ReslicedImage->GetDimensions(); // dimensions of the image int line = dims[0]; // how many pixels per line? int x = xMin; // pixel index x int y = yMin; // pixel index y // get the depth for each contour float depth = CalculateLayerDepth(renderer); vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points // We take the pointer to the first pixel of the image auto* currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel != 0)) { // check in which direction a line is necessary // a line is added if the neighbor of the current pixel has the value 0 // and if the pixel is located at the edge of the image // if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel - line) == 0) { // x direction - bottom edge of the pixel // add the 2 points vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); // add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel + line) == 0) { // x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the first pixel vvvvv if ((x > xMin || y > yMin) && *(currentPixel - 1) == 0) { // y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv not the last pixel vvvvv if ((y < yMax || (x < xMax)) && *(currentPixel + 1) == 0) { // y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } /* now consider pixels at the edge of the image */ // if vvvvv left edge of image vvvvv if (x == xMin) { // draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv right edge of image vvvvv if (x == xMax) { // draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv bottom edge of image vvvvv if (y == yMin) { // draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } // if vvvvv top edge of image vvvvv if (y == yMax) { // draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); } } // end if currentpixel is set x++; if (x > xMax) { // reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; } // end of while // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); return polyData; } void mitk::ImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_ImageActor->SetUserTransform(trans); // transform the origin to center based coordinates, because MITK is center based. localStorage->m_ImageActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); localStorage->m_ShadowOutlineActor->SetUserTransform(trans); localStorage->m_ShadowOutlineActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } bool mitk::ImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, SlicedGeometry3D *imageGeometry) { // if either one of the two geometries is nullptr we return true // for safety reasons if (renderingGeometry == nullptr || imageGeometry == nullptr) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0)); for (int i = 1; i < 8; i++) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance(cornerPoint); // if it has not the same signing as the distance of the first point if (initialDistance * distance < 0) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::ImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::ImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); // Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_ImageActor = vtkSmartPointer::New(); m_ShadowOutlineActor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_EmptyActors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); // the following actions are always the same and thus can be performed // in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); // built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY); m_BinaryLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR); m_ColorLookupTable = mitkLUT->GetVtkLookupTable(); // do not repeat the texture (the image) m_Texture->RepeatOff(); // set the mapper for the actor m_ImageActor->SetMapper(m_Mapper); m_ShadowOutlineActor->SetMapper(m_Mapper); m_Actors->AddPart(m_ShadowOutlineActor); m_Actors->AddPart(m_ImageActor); m_PublicActors = m_EmptyActors.Get(); } diff --git a/Modules/Core/src/mitkCoreServices.cpp b/Modules/Core/src/mitkCoreServices.cpp index 4470ec796c..acf5c6963f 100644 --- a/Modules/Core/src/mitkCoreServices.cpp +++ b/Modules/Core/src/mitkCoreServices.cpp @@ -1,128 +1,127 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkCoreServices.h" #include #include #include #include #include #include #include #include #include #include #include -#include -#include +#include namespace mitk { - itk::SimpleFastMutexLock &s_ContextToServicesMapMutex() + std::mutex &s_ContextToServicesMapMutex() { - static itk::SimpleFastMutexLock mutex; + static std::mutex mutex; return mutex; } std::map> &s_ContextToServicesMap() { static std::map> serviceMap; return serviceMap; } template static S *GetCoreService(us::ModuleContext *context) { if (context == nullptr) context = us::GetModuleContext(); S *coreService = nullptr; us::ServiceReference serviceRef = context->GetServiceReference(); if (serviceRef) { coreService = context->GetService(serviceRef); } assert(coreService && "Asserting non-nullptr MITK core service"); { - itk::MutexLockHolder l(s_ContextToServicesMapMutex()); + std::lock_guard l(s_ContextToServicesMapMutex()); s_ContextToServicesMap()[context].insert(std::make_pair(coreService, serviceRef)); } return coreService; } IPropertyAliases *CoreServices::GetPropertyAliases(us::ModuleContext *context) { return GetCoreService(context); } IPropertyDescriptions *CoreServices::GetPropertyDescriptions(us::ModuleContext *context) { return GetCoreService(context); } IPropertyExtensions *CoreServices::GetPropertyExtensions(us::ModuleContext *context) { return GetCoreService(context); } IPropertyFilters *CoreServices::GetPropertyFilters(us::ModuleContext *context) { return GetCoreService(context); } IPropertyPersistence *CoreServices::GetPropertyPersistence(us::ModuleContext *context) { return GetCoreService(context); } IPropertyRelations *CoreServices::GetPropertyRelations(us::ModuleContext *context) { return GetCoreService(context); } IMimeTypeProvider *CoreServices::GetMimeTypeProvider(us::ModuleContext *context) { return GetCoreService(context); } bool CoreServices::Unget(us::ModuleContext *context, const std::string & /*interfaceId*/, void *service) { bool success = false; - itk::MutexLockHolder l(s_ContextToServicesMapMutex()); + std::lock_guard l(s_ContextToServicesMapMutex()); auto iter = s_ContextToServicesMap().find(context); if (iter != s_ContextToServicesMap().end()) { auto iter2 = iter->second.find(service); if (iter2 != iter->second.end()) { us::ServiceReferenceU serviceRef = iter2->second; if (serviceRef) { success = context->UngetService(serviceRef); if (success) { iter->second.erase(iter2); } } } } return success; } } diff --git a/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp b/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp index 4935c25e5d..68f3ed89c5 100644 --- a/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp +++ b/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp @@ -1,532 +1,531 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkArbitraryTimeGeometry.h" #include "mitkGeometry3D.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include class mitkArbitraryTimeGeometryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkArbitraryTimeGeometryTestSuite); // Test the append method MITK_TEST(CountTimeSteps); MITK_TEST(GetMinimumTimePoint); MITK_TEST(GetMaximumTimePoint); MITK_TEST(GetTimeBounds); MITK_TEST(IsValidTimePoint); MITK_TEST(TimeStepToTimePoint); MITK_TEST(TimePointToTimeStep); MITK_TEST(GetGeometryCloneForTimeStep); MITK_TEST(GetGeometryForTimeStep); MITK_TEST(GetGeometryForTimePoint); MITK_TEST(IsValid); MITK_TEST(Expand); MITK_TEST(ReplaceTimeStepGeometries); MITK_TEST(ClearAllGeometries); MITK_TEST(AppendNewTimeStep); MITK_TEST(HasCollapsedFinalTimeStep); CPPUNIT_TEST_SUITE_END(); private: mitk::Geometry3D::Pointer m_Geometry1; mitk::Geometry3D::Pointer m_Geometry2; mitk::Geometry3D::Pointer m_Geometry3; mitk::Geometry3D::Pointer m_Geometry3_5; mitk::Geometry3D::Pointer m_Geometry4; mitk::Geometry3D::Pointer m_Geometry5; mitk::Geometry3D::Pointer m_InvalidGeometry; mitk::Geometry3D::Pointer m_NewGeometry; mitk::TimePointType m_Geometry1MinTP; mitk::TimePointType m_Geometry2MinTP; mitk::TimePointType m_Geometry3MinTP; mitk::TimePointType m_Geometry3_5MinTP; mitk::TimePointType m_Geometry4MinTP; mitk::TimePointType m_Geometry5MinTP; mitk::TimePointType m_NewGeometryMinTP; mitk::TimePointType m_Geometry1MaxTP; mitk::TimePointType m_Geometry2MaxTP; mitk::TimePointType m_Geometry3MaxTP; mitk::TimePointType m_Geometry3_5MaxTP; mitk::TimePointType m_Geometry4MaxTP; mitk::TimePointType m_Geometry5MaxTP; mitk::TimePointType m_NewGeometryMaxTP; mitk::ArbitraryTimeGeometry::Pointer m_emptyTimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_initTimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_12345TimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometryWithCollapsedEnd; mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometryWithCollapsedInterim; public: void setUp() override { - mitk::TimeBounds bounds; m_Geometry1 = mitk::Geometry3D::New(); m_Geometry2 = mitk::Geometry3D::New(); m_Geometry3 = mitk::Geometry3D::New(); m_Geometry3_5 = mitk::Geometry3D::New(); m_Geometry4 = mitk::Geometry3D::New(); m_Geometry5 = mitk::Geometry3D::New(); m_Geometry1MinTP = 1; m_Geometry2MinTP = 2; m_Geometry3MinTP = 3; m_Geometry3_5MinTP = 3.5; m_Geometry4MinTP = 4; m_Geometry5MinTP = 5; m_Geometry1MaxTP = 1.9; m_Geometry2MaxTP = 2.9; m_Geometry3MaxTP = 3.9; m_Geometry3_5MaxTP = 3.9; m_Geometry4MaxTP = 4.9; m_Geometry5MaxTP = 5.9; m_NewGeometry = mitk::Geometry3D::New(); m_NewGeometryMinTP = 20; m_NewGeometryMaxTP = 21.9; mitk::Point3D origin(42); m_NewGeometry->SetOrigin(origin); m_emptyTimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_emptyTimeGeometry->ClearAllGeometries(); m_initTimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_initTimeGeometry->Initialize(); m_12345TimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_12345TimeGeometry->ClearAllGeometries(); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry5, m_Geometry5MinTP, m_Geometry5MaxTP); m_123TimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_123TimeGeometry->ClearAllGeometries(); m_123TimeGeometry->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); m_123TimeGeometry->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); m_123TimeGeometry->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); m_123TimeGeometryWithCollapsedEnd = mitk::ArbitraryTimeGeometry::New(); m_123TimeGeometryWithCollapsedEnd->ClearAllGeometries(); m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MinTP); m_123TimeGeometryWithCollapsedInterim = mitk::ArbitraryTimeGeometry::New(); m_123TimeGeometryWithCollapsedInterim->ClearAllGeometries(); m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MinTP); m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); } void tearDown() override {} void CountTimeSteps() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->CountTimeSteps() == 0, "Testing CountTimeSteps with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->CountTimeSteps() == 1, "Testing CountTimeSteps with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing CountTimeSteps with m_12345TimeGeometry"); } void GetMinimumTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint() == 0.0, "Testing GetMinimumTimePoint with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMinimumTimePoint() == 0.0, "Testing GetMinimumTimePoint with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint() == 1.0, "Testing GetMinimumTimePoint with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint(2) == 0.0, "Testing GetMinimumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMinimumTimePoint(2) == 0.0, "Testing GetMinimumTimePoint(2) with m_initTimeGeometry"); /////////////////////////////////////// // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. // This workarround should be removed/reevaluated as soon as T28262 is solved and we know // how time geometries should behave in the future! MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint(2) == 3.0, "Testing GetMinimumTimePoint(2) with m_12345TimeGeometry"); // Deactivated falling original test // MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint(2) == 2.9, // "Testing GetMinimumTimePoint(2) with m_12345TimeGeometry"); // End of workarround for T27883 ////////////////////////////////////// } void GetMaximumTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint() == 0.0, "Testing GetMaximumTimePoint with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint() == 1.0, "Testing GetMaximumTimePoint with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint() == 5.9, "Testing GetMaximumTimePoint with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint(2) == 3.9, "Testing GetMaximumTimePoint(2) with m_12345TimeGeometry"); } void GetTimeBounds() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint(2) == 3.9, "Testing GetMaximumTimePoint(2) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds()[0] == 0.0, "Testing GetTimeBounds lower part with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds()[0] == 0.0, "Testing GetTimeBounds lower part with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds()[0] == 1.0, "Testing GetTimeBounds lower part with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds()[1] == 0.0, "Testing GetTimeBounds with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds()[1] == 1.0, "Testing GetTimeBounds with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds()[1] == 5.9, "Testing GetTimeBounds with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds(3)[0] == 0.0, "Testing GetTimeBounds(3) lower part with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds(3)[0] == 0.0, "Testing GetTimeBounds(3) lower part with m_initTimeGeometry"); /////////////////////////////////////// // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. // This workarround should be removed/reevaluated as soon as T28262 is solved and we know // how time geometries should behave in the future! MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[0] == 4.0, "Testing GetTimeBounds(3) lower part with m_12345TimeGeometry"); // Deactivated falling original test // MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[0] == 3.9, // "Testing GetTimeBounds(3) lower part with m_12345TimeGeometry"); // End of workarround for T27883 ////////////////////////////////////// MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds(3)[1] == 0.0, "Testing GetTimeBounds(3) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds(3)[1] == 0.0, "Testing GetTimeBounds(3) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[1] == 4.9, "Testing GetTimeBounds(3) with m_12345TimeGeometry"); } void IsValidTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(0) == false, "Testing IsValidTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(0) == true, "Testing IsValidTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(0) == false, "Testing IsValidTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(1) == false, "Testing IsValidTimePoint(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(1) == false, "Testing IsValidTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(1) == true, "Testing IsValidTimePoint(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(2.5) == false, "Testing IsValidTimePoint(2.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(2.5) == false, "Testing IsValidTimePoint(2.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(2.5) == true, "Testing IsValidTimePoint(2.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(5.89) == false, "Testing IsValidTimePoint(5.89) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(5.89) == false, "Testing IsValidTimePoint(5.89) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(5.89) == true, "Testing IsValidTimePoint(5.89) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(0) == false, "Testing IsValidTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(0) == true, "Testing IsValidTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(0) == true, "Testing IsValidTimeStep(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(1) == false, "Testing IsValidTimeStep(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(1) == false, "Testing IsValidTimeStep(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(1) == true, "Testing IsValidTimeStep(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_12345TimeGeometry"); //checked collapsed cases MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->IsValidTimePoint(m_123TimeGeometryWithCollapsedInterim->GetMaximumTimePoint()) == false, "Testing that m_123TimeGeometryWithCollapsedInterim does not inclued the max bound in validity"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->IsValidTimePoint(m_123TimeGeometryWithCollapsedEnd->GetMaximumTimePoint()) == true, "Testing that m_123TimeGeometryWithCollapsedEnd does inclued the max bound in validity, because it has an collapsed final time step. (see also T27259)"); } void TimeStepToTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(0) == 0.0, "Testing TimeStepToTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(0) == 0.0, "Testing TimeStepToTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(0) == 1.0, "Testing TimeStepToTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(1) == 0.0, "Testing TimeStepToTimePoint(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(1) == 0.0, "Testing TimeStepToTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(1) == 2.0, "Testing TimeStepToTimePoint(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_12345TimeGeometry"); } void TimePointToTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(3.5) == 0, "Testing TimePointToTimeStep(3.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(3.5) == m_initTimeGeometry->CountTimeSteps(), "Testing TimePointToTimeStep(3.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(3.5) == 2, "Testing TimePointToTimeStep(3.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(5.8) == 0, "Testing TimePointToTimeStep(5.8) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(5.8) == m_initTimeGeometry->CountTimeSteps(), "Testing TimePointToTimeStep(5.8) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(5.8) == 4, "Testing TimePointToTimeStep(5.8) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(5.9) == m_12345TimeGeometry->CountTimeSteps(), "Testing TimePointToTimeStep(5.9) with m_12345TimeGeometry"); //checked collapsed cases MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->TimePointToTimeStep(m_123TimeGeometryWithCollapsedInterim->GetMaximumTimePoint()) == m_123TimeGeometryWithCollapsedInterim->CountTimeSteps(), "Testing m_123TimeGeometryWithCollapsedInterim does not map the max time poit."); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->TimePointToTimeStep(m_123TimeGeometryWithCollapsedEnd->GetMaximumTimePoint()) == 2, "Testing that m_123TimeGeometryWithCollapsedEnd does map the max bound, because it has an collapsed final time step. (see also T27259)"); } void GetGeometryCloneForTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryCloneForTimeStep(0).IsNull(), "Testing GetGeometryCloneForTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryCloneForTimeStep(0).IsNotNull(), "Testing GetGeometryCloneForTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryCloneForTimeStep(0).IsNotNull(), "Testing GetGeometryCloneForTimeStep(0) with m_12345TimeGeometry"); } void GetGeometryForTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimeStep(0).IsNull(), "Testing GetGeometryForTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimeStep(0).IsNotNull(), "Testing GetGeometryForTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimeStep(1).IsNull(), "Testing GetGeometryForTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(0).GetPointer() == m_Geometry1.GetPointer(), "Testing GetGeometryForTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(3).GetPointer() == m_Geometry4.GetPointer(), "Testing GetGeometryForTimePoint(3) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(4).GetPointer() == m_Geometry5.GetPointer(), "Testing GetGeometryForTimePoint(4) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimeStep(5).IsNull(), "Testing GetGeometryForTimePoint(5) with m_12345TimeGeometry"); } void GetGeometryForTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimePoint(0).IsNull(), "Testing GetGeometryForTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimePoint(0).IsNotNull(), "Testing GetGeometryForTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimePoint(0).IsNull(), "Testing GetGeometryForTimeStep(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimePoint(1.5).IsNull(), "Testing GetGeometryForTimeStep(1.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimePoint(1.5).IsNull(), "Testing GetGeometryForTimeStep(1.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimePoint(1.5).GetPointer() == m_Geometry1.GetPointer(), "Testing GetGeometryForTimeStep(1.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimePoint(3.5).GetPointer() == m_Geometry3.GetPointer(), "Testing GetGeometryForTimeStep(3.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimePoint(5.9).IsNull(), "Testing GetGeometryForTimeStep(5.9) with m_12345TimeGeometry"); } void IsValid() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValid() == false, "Testing IsValid() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValid() == true, "Testing IsValid() with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValid() == true, "Testing IsValid() with m_12345TimeGeometry"); } void Expand() { m_12345TimeGeometry->Expand(3); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing Expand(3) doesn't change m_12345TimeGeometry"); m_12345TimeGeometry->Expand(7); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 7, "Testing Expand(7) with m_12345TimeGeometry"); } void ReplaceTimeStepGeometries() { // Test replace time step geometries m_12345TimeGeometry->ReplaceTimeStepGeometries(m_NewGeometry); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing ReplaceTimeStepGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(0)->GetOrigin() == m_NewGeometry->GetOrigin(), "Testing ReplaceTimeStepGeometries(): check if first geometry of m_12345TimeGeometry " "was replaced m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(4)->GetOrigin() == m_NewGeometry->GetOrigin(), "Testing ReplaceTimeStepGeometries(): check if last geometry of m_12345TimeGeometry " "was replaced m_12345TimeGeometry"); } void ClearAllGeometries() { // Test clear all geometries m_12345TimeGeometry->ClearAllGeometries(); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); } void AppendNewTimeStep() { // Test append MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(nullptr, 0, 1)); MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(m_Geometry3_5,m_Geometry3_5MinTP,m_Geometry3_5MaxTP)); MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MaxTP, m_Geometry4MinTP)); //valid but inverted bounds m_emptyTimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->CountTimeSteps() == 1, "Testing AppendNewTimeStep() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint() == 4, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint() == 4.9, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->CountTimeSteps() == 3, "Testing AppendNewTimeStep() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint() == 1, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMaximumTimePoint() == 3.9, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); m_123TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->CountTimeSteps() == 4, "Testing AppendNewTimeStep() with m_123TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint() == 1, "Testing AppendNewTimeStep() with m_123TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMaximumTimePoint() == 4.9, "Testing AppendNewTimeStep() with m_123TimeGeometry"); /////////////////////////////////////// // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. // This workarround should be removed/reevaluated as soon as T28262 is solved and we know // how time geometries should behave in the future! MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint(3) == 4.0, "Testing AppendNewTimeStep() with m_123TimeGeometry"); // Deactivated falling original test // MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint(3) == 3.9, // "Testing AppendNewTimeStep() with m_123TimeGeometry"); // End of workarround for T27883 ////////////////////////////////////// } void HasCollapsedFinalTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->HasCollapsedFinalTimeStep() == true, "Testing HasCollapsedFinalTimeStep() with m_123TimeGeometryWithCollapsedEnd"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_123TimeGeometryWithCollapsedInterim"); } }; MITK_TEST_SUITE_REGISTRATION(mitkArbitraryTimeGeometry) diff --git a/Modules/Core/test/mitkBaseGeometryTest.cpp b/Modules/Core/test/mitkBaseGeometryTest.cpp index f4907791b2..b975f48661 100644 --- a/Modules/Core/test/mitkBaseGeometryTest.cpp +++ b/Modules/Core/test/mitkBaseGeometryTest.cpp @@ -1,1681 +1,1680 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTestingMacros.h" #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; typedef itk::BoundingBox BoundingBox; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // Dummy instance of abstract base class class DummyTestClass : public mitk::BaseGeometry { public: DummyTestClass(){}; DummyTestClass(const DummyTestClass &other) : BaseGeometry(other){}; ~DummyTestClass() override{}; mitkClassMacro(DummyTestClass, mitk::BaseGeometry); itkNewMacro(Self); mitkNewMacro1Param(Self, const Self &); itk::LightObject::Pointer InternalClone() const override { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } protected: void PrintSelf(std::ostream & /*os*/, itk::Indent /*indent*/) const override{}; //##Documentation //## @brief Pre- and Post-functions are empty in BaseGeometry //## //## These virtual functions allow for a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use {}. //## If this class is inherited from a subclass of BaseGeometry, call {Superclass::Pre...();};, example: // SlicedGeometry3D class void PreSetSpacing(const mitk::Vector3D &/*aSpacing*/) override{}; }; class mitkBaseGeometryTestSuite : public mitk::TestFixture { // List of Tests CPPUNIT_TEST_SUITE(mitkBaseGeometryTestSuite); // Constructor MITK_TEST(TestConstructors); MITK_TEST(TestInitialize); // Set MITK_TEST(TestSetOrigin); MITK_TEST(TestSetBounds); MITK_TEST(TestSetFloatBounds); MITK_TEST(TestSetFloatBoundsDouble); MITK_TEST(TestSetFrameOfReferenceID); MITK_TEST(TestSetIndexToWorldTransform); MITK_TEST(TestSetIndexToWorldTransformWithoutChangingSpacing); MITK_TEST(TestSetIndexToWorldTransform_WithPointerToSameTransform); MITK_TEST(TestSetSpacing); MITK_TEST(TestTransferItkToVtkTransform); MITK_TEST(TestSetIndexToWorldTransformByVtkMatrix); MITK_TEST(TestSetIdentity); MITK_TEST(TestSetImageGeometry); // Equal MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue); MITK_TEST(Equal_DifferentOrigin_ReturnsFalse); MITK_TEST(Equal_DifferentIndexToWorldTransform_ReturnsFalse); MITK_TEST(Equal_DifferentSpacing_ReturnsFalse); MITK_TEST(Equal_InputIsNull_ReturnsFalse); MITK_TEST(Equal_DifferentBoundingBox_ReturnsFalse); MITK_TEST(Equal_Transforms_MinorDifferences_And_Eps); // other Functions MITK_TEST(TestComposeTransform); MITK_TEST(TestComposeVtkMatrix); MITK_TEST(TestTranslate); MITK_TEST(TestIndexToWorld); MITK_TEST(TestExecuteOperation); MITK_TEST(TestCalculateBoundingBoxRelToTransform); // MITK_TEST(TestSetTimeBounds); MITK_TEST(TestIs2DConvertable); MITK_TEST(TestGetCornerPoint); MITK_TEST(TestExtentInMM); MITK_TEST(TestGetAxisVector); MITK_TEST(TestGetCenter); MITK_TEST(TestGetDiagonalLength); MITK_TEST(TestGetExtent); MITK_TEST(TestIsInside); MITK_TEST(TestGetMatrixColumn); // test IsSubGeometry MITK_TEST(IsSubGeometry_Spacing); MITK_TEST(IsSubGeometry_TransformMatrix); MITK_TEST(IsSubGeometry_Bounds_Image); MITK_TEST(IsSubGeometry_Bounds_NoneImage); MITK_TEST(IsSubGeometry_Grid_Image); MITK_TEST(IsSubGeometry_Grid_NoneImage); MITK_TEST(IsSubGeometry_Bounds_Oblique_Image); MITK_TEST(IsSubGeometry_Bounds_Oblique_NoneImage); MITK_TEST(IsSubGeometry_Grid_Oblique_Image); MITK_TEST(IsSubGeometry_Grid_Oblique_NoneImage); CPPUNIT_TEST_SUITE_END(); // Used Variables private: mitk::Point3D aPoint; float aFloatSpacing[3]; mitk::Vector3D aSpacing; mitk::AffineTransform3D::Pointer aTransform; BoundingBoxPointer aBoundingBox; mitk::AffineTransform3D::MatrixType aMatrix; mitk::Point3D anotherPoint; mitk::Vector3D anotherSpacing; BoundingBoxPointer anotherBoundingBox; BoundingBoxPointer aThirdBoundingBox; mitk::AffineTransform3D::Pointer anotherTransform; mitk::AffineTransform3D::Pointer aThirdTransform; mitk::AffineTransform3D::MatrixType anotherMatrix; mitk::AffineTransform3D::MatrixType aThirdMatrix; DummyTestClass::Pointer aDummyGeometry; DummyTestClass::Pointer anotherDummyGeometry; DummyTestClass::Pointer aDummyGeometryOblique; public: // Set up for variables void setUp() override { mitk::FillVector3D(aFloatSpacing, 1, 1, 1); mitk::FillVector3D(aSpacing, 1, 1, 1); mitk::FillVector3D(aPoint, 0, 0, 0); // Transform aTransform = mitk::AffineTransform3D::New(); aTransform->SetIdentity(); aMatrix.SetIdentity(); anotherTransform = mitk::AffineTransform3D::New(); anotherMatrix.SetIdentity(); anotherMatrix(1, 1) = 2; anotherTransform->SetMatrix(anotherMatrix); aThirdTransform = mitk::AffineTransform3D::New(); aThirdMatrix.SetIdentity(); aThirdMatrix(1, 1) = 7; aThirdTransform->SetMatrix(aThirdMatrix); // Bounding Box float bounds[6] = { 0, 1, 0, 1, 0, 1 }; mitk::BoundingBox::BoundsArrayType b; const float* input = bounds; int j = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); j < 6; ++j) *it++ = (mitk::ScalarType) * input++; aBoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < 3; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } aBoundingBox->SetPoints(pointscontainer); aBoundingBox->ComputeBoundingBox(); anotherBoundingBox = BoundingBoxType::New(); p[0] = 11; p[1] = 12; p[2] = 13; pointscontainer->InsertElement(1, p); anotherBoundingBox->SetPoints(pointscontainer); anotherBoundingBox->ComputeBoundingBox(); aThirdBoundingBox = BoundingBoxType::New(); p[0] = 22; p[1] = 23; p[2] = 24; pointscontainer->InsertElement(1, p); aThirdBoundingBox->SetPoints(pointscontainer); aThirdBoundingBox->ComputeBoundingBox(); mitk::FillVector3D(anotherPoint, 2, 3, 4); mitk::FillVector3D(anotherSpacing, 5, 6.5, 7); aDummyGeometry = DummyTestClass::New(); aDummyGeometry->Initialize(); anotherDummyGeometry = aDummyGeometry->Clone(); aDummyGeometryOblique = DummyTestClass::New(); aDummyGeometryOblique->Initialize(); auto newBounds = aDummyGeometryOblique->GetBounds(); newBounds[0] = 0; newBounds[1] = 5; newBounds[2] = 10; newBounds[3] = 20; newBounds[4] = 30; newBounds[5] = 40; aDummyGeometryOblique->SetBounds(newBounds); aDummyGeometryOblique->GetMatrixColumn(0); auto obliqueTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::OutputVectorType rotationAxis(0.); rotationAxis[1] = 1.; obliqueTransform->Rotate3D(rotationAxis, 0.6); mitk::AffineTransform3D::OutputVectorType translation; translation[0] = 100.; translation[1] = -50.; translation[2] = -150.; obliqueTransform->SetTranslation(translation); aDummyGeometryOblique->SetIndexToWorldTransform(obliqueTransform); } void tearDown() override { aDummyGeometry = nullptr; anotherDummyGeometry = nullptr; } // Test functions void TestSetOrigin() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); // undo changes, new and changed object need to be the same! dummy->SetOrigin(aPoint); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetOrigin"); } void TestSetImageGeometry() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetImageGeometry(true); CPPUNIT_ASSERT(dummy->GetImageGeometry()); // undo changes, new and changed object need to be the same! dummy->SetImageGeometry(false); CPPUNIT_ASSERT(dummy->GetImageGeometry() == false); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetImageGeometry"); } void TestSetFloatBounds() { float bounds[6] = {0, 11, 0, 12, 0, 13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox equality"); // Wrong bounds, test needs to fail bounds[1] = 7; dummy->SetFloatBounds(bounds); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox not equal"); // undo changes, new and changed object need to be the same! float originalBounds[6] = {0, 1, 0, 1, 0, 1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo and equal"); } void TestSetBounds() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetBounds(anotherBoundingBox->GetBounds()); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting bounds"); // Test needs to fail now dummy->SetBounds(aThirdBoundingBox->GetBounds()); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting unequal bounds"); // undo changes, new and changed object need to be the same! dummy->SetBounds(aBoundingBox->GetBounds()); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set bounds"); } void TestSetFloatBoundsDouble() { double bounds[6] = {0, 11, 0, 12, 0, 13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds"); // Test needs to fail now bounds[3] = 7; dummy->SetFloatBounds(bounds); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds unequal"); // undo changes, new and changed object need to be the same! double originalBounds[6] = {0, 1, 0, 1, 0, 1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set float bounds"); } void TestSetFrameOfReferenceID() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFrameOfReferenceID(5); CPPUNIT_ASSERT(dummy->GetFrameOfReferenceID() == 5); // undo changes, new and changed object need to be the same! dummy->SetFrameOfReferenceID(0); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set frame of reference"); } void TestSetIndexToWorldTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransform 1"); // Test needs to fail now dummy->SetIndexToWorldTransform(aThirdTransform); MITK_ASSERT_NOT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransform 2"); // undo changes, new and changed object need to be the same! dummy->SetIndexToWorldTransform(aTransform); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransform 3"); } void TestSetIndexToWorldTransformWithoutChangingSpacing() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransformWithoutChangingSpacing(anotherTransform); CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing(), mitk::eps, true)); // calculate a new version of anotherTransform, so that the spacing should be the same as the original spacing of // aTransform. mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = anotherTransform->GetMatrix().GetVnlMatrix(); mitk::VnlVector col; - col = vnlmatrix.get_column(0); + col = vnlmatrix.get_column(0).as_ref(); col.normalize(); col *= aSpacing[0]; vnlmatrix.set_column(0, col); - col = vnlmatrix.get_column(1); + col = vnlmatrix.get_column(1).as_ref(); col.normalize(); col *= aSpacing[1]; vnlmatrix.set_column(1, col); - col = vnlmatrix.get_column(2); + col = vnlmatrix.get_column(2).as_ref(); col.normalize(); col *= aSpacing[2]; vnlmatrix.set_column(2, col); mitk::Matrix3D matrix; matrix = vnlmatrix; anotherTransform->SetMatrix(matrix); CPPUNIT_ASSERT(mitk::Equal(*anotherTransform, *(dummy->GetIndexToWorldTransform()), mitk::eps, true)); } void TestSetIndexToWorldTransform_WithPointerToSameTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetSpacing(anotherSpacing); mitk::AffineTransform3D::Pointer testTransfrom = dummy->GetIndexToWorldTransform(); mitk::Vector3D modifiedPoint = anotherPoint.GetVectorFromOrigin() * 2.; testTransfrom->SetOffset(modifiedPoint); dummy->SetIndexToWorldTransform(testTransfrom); CPPUNIT_ASSERT(mitk::Equal(modifiedPoint, dummy->GetOrigin().GetVectorFromOrigin())); } void TestSetIndexToWorldTransformByVtkMatrix() { vtkMatrix4x4 *vtkmatrix; vtkmatrix = vtkMatrix4x4::New(); vtkmatrix->Identity(); vtkmatrix->SetElement(1, 1, 2); DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); MITK_ASSERT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransformByVtkMatrix 1"); // test needs to fail now vtkmatrix->SetElement(1, 1, 7); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); MITK_ASSERT_NOT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransformByVtkMatrix 2"); // undo changes, new and changed object need to be the same! vtkmatrix->SetElement(1, 1, 1); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); vtkmatrix->Delete(); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransformByVtkMatrix 3"); } void TestSetIdentity() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // Change IndextoWorldTransform and Origin dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetOrigin(anotherPoint); // Set Identity should reset ITWT and Origin dummy->SetIdentity(); MITK_ASSERT_EQUAL( aTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Test set identity 1"); CPPUNIT_ASSERT(mitk::Equal(aPoint, dummy->GetOrigin())); CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing())); // new and changed object need to be the same! DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Test set identity 2"); } void TestSetSpacing() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing())); // undo changes, new and changed object need to be the same! dummy->SetSpacing(aSpacing); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy spacing"); } void TestTransferItkToVtkTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); // calls TransferItkToVtkTransform mitk::AffineTransform3D::Pointer dummyTransform = dummy->GetIndexToWorldTransform(); CPPUNIT_ASSERT(mitk::MatrixEqualElementWise(anotherMatrix, dummyTransform->GetMatrix())); } void TestConstructors() { // test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); bool test = dummy1->IsValid(); CPPUNIT_ASSERT(test == true); CPPUNIT_ASSERT(dummy1->GetFrameOfReferenceID() == 0); CPPUNIT_ASSERT(dummy1->GetIndexToWorldTransformLastModified() == 0); CPPUNIT_ASSERT(mitk::Equal(dummy1->GetSpacing(), aSpacing)); CPPUNIT_ASSERT(mitk::Equal(dummy1->GetOrigin(), aPoint)); CPPUNIT_ASSERT(dummy1->GetImageGeometry() == false); MITK_ASSERT_EQUAL( mitk::AffineTransform3D::Pointer(dummy1->GetIndexToWorldTransform()), aTransform, "Contructor test 1"); MITK_ASSERT_EQUAL( mitk::BaseGeometry::BoundingBoxType::ConstPointer(dummy1->GetBoundingBox()), aBoundingBox, "Constructor test 2"); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); float bounds[6] = {0, 11, 0, 12, 0, 13}; dummy2->SetFloatBounds(bounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); DummyTestClass::Pointer dummy3 = DummyTestClass::New(*dummy2); MITK_ASSERT_EQUAL(dummy3, dummy2, "Dummy contructor"); } // Equal Tests void Equal_CloneAndOriginal_ReturnsTrue() { MITK_ASSERT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Clone test"); } void Equal_DifferentOrigin_ReturnsFalse() { anotherDummyGeometry->SetOrigin(anotherPoint); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different origin test"); } void Equal_DifferentIndexToWorldTransform_ReturnsFalse() { anotherDummyGeometry->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different index to world"); } void Equal_DifferentSpacing_ReturnsFalse() { anotherDummyGeometry->SetSpacing(anotherSpacing); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different spacing"); } void Equal_InputIsNull_ReturnsFalse() { DummyTestClass::Pointer geometryNull = nullptr; CPPUNIT_ASSERT_THROW(MITK_ASSERT_EQUAL(geometryNull, anotherDummyGeometry, "Input is null"), mitk::Exception); } void Equal_DifferentBoundingBox_ReturnsFalse() { // create different bounds to make the comparison false mitk::ScalarType bounds[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; anotherDummyGeometry->SetBounds(bounds); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different bounding box"); } void Equal_Transforms_MinorDifferences_And_Eps() { // Verifies that the eps parameter is evaluated properly // when comparing two mitk::BaseGeometry::TransformTypes aMatrix.SetIdentity(); anotherMatrix.SetIdentity(); aMatrix(0, 1) = 0.0002; aTransform->SetMatrix(aMatrix); anotherMatrix(0, 1) = 0.0002; anotherTransform->SetMatrix(anotherMatrix); anotherTransform->SetMatrix(aMatrix); CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=mitk::eps", mitk::Equal(*aTransform, *anotherTransform, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=vnl_math::eps", mitk::Equal(*aTransform, *anotherTransform, vnl_math::eps, true)); anotherMatrix(0, 1) = 0.0002 + mitk::eps; anotherTransform->SetMatrix(anotherMatrix); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=vnl_math::eps", !mitk::Equal(*aTransform, *anotherTransform, vnl_math::eps, true)); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=mitk::eps-1%", !mitk::Equal(*aTransform, *anotherTransform, mitk::eps * 0.99, true)); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps _are_ mitk::Equal() for eps=mitk::eps+1%", mitk::Equal(*aTransform, *anotherTransform, mitk::eps * 1.01, true)); } void TestComposeTransform() { // Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1, 1) = 2; transform1->SetMatrix(matrix1); // Spacing = 2 mitk::AffineTransform3D::Pointer transform2; transform2 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix2; matrix2.SetIdentity(); matrix2(1, 1) = 2; transform2->SetMatrix(matrix2); // Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1, 1) = 4; transform3->SetMatrix(matrix3); // Spacing = 4 mitk::AffineTransform3D::Pointer transform4; transform4 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix4; matrix4.SetIdentity(); matrix4(1, 1) = 0.25; transform4->SetMatrix(matrix4); // Spacing = 0.25 // Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); // Spacing = 2 dummy->Compose(transform2); // Spacing = 4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); MITK_ASSERT_EQUAL( transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose transform 2"); // 4=4 // undo changes, new and changed object need to be the same! dummy->Compose(transform4); // Spacing = 1 DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compose transform 3"); // 1=1 } void TestComposeVtkMatrix() { // Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1, 1) = 2; transform1->SetMatrix(matrix1); // Spacing = 2 vtkMatrix4x4 *vtkmatrix2; vtkmatrix2 = vtkMatrix4x4::New(); vtkmatrix2->Identity(); vtkmatrix2->SetElement(1, 1, 2); // Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1, 1) = 4; transform3->SetMatrix(matrix3); // Spacing = 4 vtkMatrix4x4 *vtkmatrix4; vtkmatrix4 = vtkMatrix4x4::New(); vtkmatrix4->Identity(); vtkmatrix4->SetElement(1, 1, 0.25); // Spacing = 0.25 // Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); // Spacing = 2 dummy->Compose(vtkmatrix2); // Spacing = 4 vtkmatrix2->Delete(); MITK_ASSERT_EQUAL( transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose vtk matrix"); // 4=4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); // undo changes, new and changed object need to be the same! dummy->Compose(vtkmatrix4); // Spacing = 1 vtkmatrix4->Delete(); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compose vtk"); // 1=1 } void TestTranslate() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); // use some random values for translation mitk::Vector3D translationVector; translationVector.SetElement(0, 17.5f); translationVector.SetElement(1, -32.3f); translationVector.SetElement(2, 4.0f); // compute ground truth mitk::Point3D tmpResult = anotherPoint + translationVector; dummy->Translate(translationVector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), tmpResult)); // undo changes translationVector *= -1; dummy->Translate(translationVector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), anotherPoint)); // undo changes, new and changed object need to be the same! translationVector.SetElement(0, -1 * anotherPoint[0]); translationVector.SetElement(1, -1 * anotherPoint[1]); translationVector.SetElement(2, -1 * anotherPoint[2]); dummy->Translate(translationVector); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Translate test"); } // a part of the test requires axis-parallel coordinates int testIndexAndWorldConsistency(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems mitk::Point3D origin = dummyGeometry->GetOrigin(); mitk::Point3D dummyPoint; // Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(origin, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, origin, 3, mitk::eps, true)); // Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0) mitk::Point3D globalOrigin; mitk::FillVector3D(globalOrigin, 0, 0, 0); mitk::Point3D originContinuousIndex; dummyGeometry->WorldToIndex(origin, originContinuousIndex); CPPUNIT_ASSERT(mitk::EqualArray(originContinuousIndex, globalOrigin, 3, mitk::eps, true)); // Testing WorldToIndex(origin, itk::Index)==(0,0,0) itk::Index<3> itkindex; dummyGeometry->WorldToIndex(origin, itkindex); itk::Index<3> globalOriginIndex; mitk::vtk2itk(globalOrigin, globalOriginIndex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0) mitk::Vector3D halfSpacingStep = dummyGeometry->GetSpacing() * 0.5; mitk::Matrix3D rotation; mitk::Point3D originOffCenter = origin - halfSpacingStep; dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0) originOffCenter = origin + halfSpacingStep; originOffCenter -= 0.0001; dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); originOffCenter = origin + halfSpacingStep; itk::Index<3> global111; mitk::FillVector3D(global111, 1, 1, 1); dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, global111, 3, mitk::eps, true)); // Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter mitk::Point3D center = dummyGeometry->GetCenter(); mitk::Point3D centerContIndex; dummyGeometry->WorldToIndex(center, centerContIndex); mitk::BoundingBox::ConstPointer boundingBox = dummyGeometry->GetBoundingBox(); mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter(); CPPUNIT_ASSERT(mitk::Equal(centerContIndex, centerBounds)); // Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter) center = dummyGeometry->GetCenter(); mitk::Point3D centerBoundsInWorldCoords; dummyGeometry->IndexToWorld(centerBounds, centerBoundsInWorldCoords); CPPUNIT_ASSERT(mitk::Equal(center, centerBoundsInWorldCoords)); // Test using random point, // Testing consistency of index and world coordinate systems mitk::Point3D point; mitk::FillVector3D(point, 3.5, -2, 4.6); // Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(point, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, point, 3, mitk::eps, true)); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForVectors(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems for vectors mitk::Vector3D xAxisMM = dummyGeometry->GetAxisVector(0); mitk::Vector3D xAxisContinuousIndex; mitk::Point3D p, pIndex, origin; origin = dummyGeometry->GetOrigin(); p[0] = xAxisMM[0] + origin[0]; p[1] = xAxisMM[1] + origin[1]; p[2] = xAxisMM[2] + origin[2]; dummyGeometry->WorldToIndex(p, pIndex); dummyGeometry->WorldToIndex(xAxisMM, xAxisContinuousIndex); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], pIndex[0])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], pIndex[1])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], pIndex[2])); dummyGeometry->IndexToWorld(xAxisContinuousIndex, xAxisContinuousIndex); dummyGeometry->IndexToWorld(pIndex, p); CPPUNIT_ASSERT(xAxisContinuousIndex == xAxisMM); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], p[0] - origin[0])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], p[1] - origin[1])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], p[2] - origin[2])); // Test consictency for random vector mitk::Vector3D vector; mitk::FillVector3D(vector, 2.5, -3.2, 8.1); mitk::Vector3D vectorContinuousIndex; p[0] = vector[0] + origin[0]; p[1] = vector[1] + origin[1]; p[2] = vector[2] + origin[2]; dummyGeometry->WorldToIndex(p, pIndex); dummyGeometry->WorldToIndex(vector, vectorContinuousIndex); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], pIndex[0])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], pIndex[1])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], pIndex[2])); dummyGeometry->IndexToWorld(vectorContinuousIndex, vectorContinuousIndex); dummyGeometry->IndexToWorld(pIndex, p); CPPUNIT_ASSERT(vectorContinuousIndex == vector); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], p[0] - origin[0])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], p[1] - origin[1])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], p[2] - origin[2])); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForIndex(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems // creating testing data itk::Index<4> itkIndex4, itkIndex4b; itk::Index<3> itkIndex3, itkIndex3b; itk::Index<2> itkIndex2, itkIndex2b; itk::Index<3> mitkIndex, mitkIndexb; itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4; itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6; itkIndex2[0] = itkIndex2[1] = 2; mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13; // check for constistency mitk::Point3D point; dummyGeometry->IndexToWorld(itkIndex2, point); dummyGeometry->WorldToIndex(point, itkIndex2b); CPPUNIT_ASSERT(((itkIndex2b[0] == itkIndex2[0]) && (itkIndex2b[1] == itkIndex2[1]))); // Testing itk::index<2> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex3, point); dummyGeometry->WorldToIndex(point, itkIndex3b); CPPUNIT_ASSERT( ((itkIndex3b[0] == itkIndex3[0]) && (itkIndex3b[1] == itkIndex3[1]) && (itkIndex3b[2] == itkIndex3[2]))); // Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex4, point); dummyGeometry->WorldToIndex(point, itkIndex4b); CPPUNIT_ASSERT(((itkIndex4b[0] == itkIndex4[0]) && (itkIndex4b[1] == itkIndex4[1]) && (itkIndex4b[2] == itkIndex4[2]) && (itkIndex4b[3] == 0))); // Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(mitkIndex, point); dummyGeometry->WorldToIndex(point, mitkIndexb); CPPUNIT_ASSERT( ((mitkIndexb[0] == mitkIndex[0]) && (mitkIndexb[1] == mitkIndex[1]) && (mitkIndexb[2] == mitkIndex[2]))); // Testing mitk::Index for IndexToWorld/WorldToIndex consistency return EXIT_SUCCESS; } void TestIndexToWorld() { DummyTestClass::Pointer dummy = DummyTestClass::New(); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); // Geometry must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy index to world"); // Test with other geometries dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetIndexToWorldTransform(anotherTransform); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetSpacing(anotherSpacing); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); } void TestExecuteOperation() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // Do same Operations with new Dummy and compare DummyTestClass::Pointer newDummy = DummyTestClass::New(); // Test operation Nothing auto opN = new mitk::Operation(mitk::OpNOTHING); dummy->ExecuteOperation(opN); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 1"); // Test operation Move auto opP = new mitk::PointOperation(mitk::OpMOVE, anotherPoint); dummy->ExecuteOperation(opP); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); newDummy->SetOrigin(anotherPoint); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 2"); // Test operation Scale, Scale sets spacing to scale+1 mitk::Point3D spacing; spacing[0] = anotherSpacing[0] - 1.; spacing[1] = anotherSpacing[1] - 1.; spacing[2] = anotherSpacing[2] - 1.; auto opS = new mitk::ScaleOperation(mitk::OpSCALE, spacing, anotherPoint); dummy->ExecuteOperation(opS); CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing())); newDummy->SetSpacing(anotherSpacing); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 3"); // change Geometry to test more cases dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetSpacing(anotherSpacing); // Testing a rotation of the geometry double angle = 35.0; mitk::Vector3D rotationVector; mitk::FillVector3D(rotationVector, 1, 0, 0); mitk::Point3D center = dummy->GetCenter(); auto opR = new mitk::RotationOperation(mitk::OpROTATE, center, rotationVector, angle); dummy->ExecuteOperation(opR); mitk::Matrix3D rotation; mitk::GetRotation(dummy, rotation); mitk::Vector3D voxelStep = rotation * anotherSpacing; mitk::Vector3D voxelStepIndex; dummy->WorldToIndex(voxelStep, voxelStepIndex); mitk::Vector3D expectedVoxelStepIndex; expectedVoxelStepIndex.Fill(1); CPPUNIT_ASSERT(mitk::Equal(voxelStepIndex, expectedVoxelStepIndex)); delete opR; delete opN; delete opS; delete opP; } void TestCalculateBoundingBoxRelToTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0, 15); dummy->SetExtentInMM(1, 20); dummy->SetExtentInMM(2, 8); mitk::BoundingBox::Pointer dummyBoundingBox = dummy->CalculateBoundingBoxRelativeToTransform(anotherTransform); mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid = 0; unsigned char i; mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); anotherTransform->GetInverse(inverse); for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, inverse->TransformPoint(dummy->GetCornerPoint(i))); mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); MITK_ASSERT_EQUAL(result, dummyBoundingBox, "BBox rel to transform"); // dummy still needs to be unchanged, except for extend DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetExtentInMM(0, 15); newDummy->SetExtentInMM(1, 20); newDummy->SetExtentInMM(2, 8); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy BBox"); } // void TestSetTimeBounds(){ // mitk::TimeBounds timeBounds; // timeBounds[0] = 1; // timeBounds[1] = 9; // DummyTestClass::Pointer dummy = DummyTestClass::New(); // dummy->SetTimeBounds(timeBounds); // mitk::TimeBounds timeBounds2 = dummy->GetTimeBounds(); // CPPUNIT_ASSERT(timeBounds[0]==timeBounds2[0]); // CPPUNIT_ASSERT(timeBounds[1]==timeBounds2[1]); // //undo changes, new and changed object need to be the same! // timeBounds[0]=mitk::ScalarTypeNumericTraits::NonpositiveMin(); // timeBounds[1]=mitk::ScalarTypeNumericTraits::max(); // DummyTestClass::Pointer newDummy = DummyTestClass::New(); // CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); //} void TestIs2DConvertable() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // new initialized geometry is 2D convertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); // Wrong Spacing needs to fail dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo dummy->SetSpacing(aSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable()); // Wrong Origin needs to fail dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo dummy->SetOrigin(aPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable()); // third dimension must not be transformed mitk::AffineTransform3D::Pointer dummyTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType dummyMatrix; dummyMatrix.SetIdentity(); dummyTransform->SetMatrix(dummyMatrix); dummy->SetIndexToWorldTransform(dummyTransform); // identity matrix is 2DConvertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); dummyMatrix(0, 2) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(1, 2) = 0.4; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 2) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 1) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 0) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo changes, new and changed object need to be the same! dummyMatrix.SetIdentity(); dummyTransform->SetMatrix(dummyMatrix); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Is 2D convertable"); } void TestGetCornerPoint() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 0, 12, 0, 13}; dummy->SetFloatBounds(bounds); mitk::Point3D corner, refCorner; // Corner 0 mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(0); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, true, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 1 mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(1); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, true, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 2 mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(2); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, false, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 3 mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(3); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, false, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 4 mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(4); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, true, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 5 mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(5); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, true, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 6 mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(6); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, false, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 7 mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(7); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, false, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Wrong Corner needs to fail CPPUNIT_ASSERT_THROW(dummy->GetCornerPoint(20), itk::ExceptionObject); // dummy geometry must not have changed! DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Corner point"); } void TestExtentInMM() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0, 50); CPPUNIT_ASSERT(mitk::Equal(50., dummy->GetExtentInMM(0))); // Vnl Matrix has changed. The next line only works because the spacing is 1! CPPUNIT_ASSERT( mitk::Equal(50., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude())); // Smaller extent than original dummy->SetExtentInMM(0, 5); CPPUNIT_ASSERT(mitk::Equal(5., dummy->GetExtentInMM(0))); CPPUNIT_ASSERT( mitk::Equal(5., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude())); dummy->SetExtentInMM(1, 4); CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtentInMM(1))); CPPUNIT_ASSERT( mitk::Equal(4., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).magnitude())); dummy->SetExtentInMM(2, 2.5); CPPUNIT_ASSERT(mitk::Equal(2.5, dummy->GetExtentInMM(2))); CPPUNIT_ASSERT( mitk::Equal(2.5, dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).magnitude())); } void TestGetAxisVector() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 0, 12, 0, 13}; dummy->SetFloatBounds(bounds); mitk::Vector3D vector; mitk::FillVector3D(vector, bounds[1], 0, 0); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(0), vector)); mitk::FillVector3D(vector, 0, bounds[3], 0); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(1), vector)); mitk::FillVector3D(vector, 0, 0, bounds[5]); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(2), vector)); } void TestGetCenter() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 2, 12, 1, 13}; dummy->SetFloatBounds(bounds); mitk::Point3D refCenter; for (int i = 0; i < 3; i++) refCenter.SetElement(i, (bounds[2 * i] + bounds[2 * i + 1]) / 2.0); dummy->IndexToWorld(refCenter, refCenter); CPPUNIT_ASSERT(mitk::Equal(dummy->GetCenter(), refCenter)); } void TestGetDiagonalLength() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); // 3-1=2, 8-5=3, 11.5-7.5=4; 2^2+3^2+4^2 = 29 double expectedLength = sqrt(29.); CPPUNIT_ASSERT(mitk::Equal(expectedLength, dummy->GetDiagonalLength(), mitk::eps, true)); CPPUNIT_ASSERT(mitk::Equal(29., dummy->GetDiagonalLength2(), mitk::eps, true)); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Diagonal length"); } void TestGetExtent() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal(2., dummy->GetExtent(0))); CPPUNIT_ASSERT(mitk::Equal(3., dummy->GetExtent(1))); CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtent(2))); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Extend"); } void TestIsInside() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); mitk::Point3D insidePoint; mitk::Point3D outsidePoint; mitk::FillVector3D(insidePoint, 2, 6, 7.6); mitk::FillVector3D(outsidePoint, 0, 9, 8.2); CPPUNIT_ASSERT(dummy->IsIndexInside(insidePoint)); CPPUNIT_ASSERT(false == dummy->IsIndexInside(outsidePoint)); dummy->IndexToWorld(insidePoint, insidePoint); dummy->IndexToWorld(outsidePoint, outsidePoint); CPPUNIT_ASSERT(dummy->IsInside(insidePoint)); CPPUNIT_ASSERT(false == dummy->IsInside(outsidePoint)); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Is inside"); } void TestInitialize() { // test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); dummy2->SetBounds(anotherBoundingBox->GetBounds()); // mitk::TimeBounds timeBounds; // timeBounds[0] = 1; // timeBounds[1] = 9; // dummy2->SetTimeBounds(timeBounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); dummy1->InitializeGeometry(dummy2); MITK_ASSERT_EQUAL(dummy1, dummy2, "Initialize 1"); dummy1->Initialize(); DummyTestClass::Pointer dummy3 = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy3, dummy1, "Initialize 2"); } void TestGetMatrixColumn() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); mitk::Vector3D testVector, refVector; testVector.SetVnlVector(dummy->GetMatrixColumn(0)); mitk::FillVector3D(refVector, 1, 0, 0); CPPUNIT_ASSERT(testVector == refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(1)); mitk::FillVector3D(refVector, 0, 2, 0); CPPUNIT_ASSERT(testVector == refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(2)); mitk::FillVector3D(refVector, 0, 0, 1); CPPUNIT_ASSERT(testVector == refVector); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_EQUAL(dummy, newDummy, "GetMatrixColumn"); } void IsSubGeometry_Spacing() { CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing(); wrongSpacing[i] += mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } for (unsigned int i = 0; i < 3; ++i) { mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing(); wrongSpacing[i] -= mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetSpacing(wrongSpacing); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } void IsSubGeometry_TransformMatrix() { CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 3; ++i) { for (unsigned int j = 0; j < 3; ++j) { itk::Matrix wrongMatrix = aDummyGeometry->GetIndexToWorldTransform()->GetMatrix(); wrongMatrix[i][j] += mitk::eps * 2; auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->GetIndexToWorldTransform()->SetMatrix(wrongMatrix); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } } void IsSubGeometry_Bounds_NoneImage() { IsSubGeometry_Bounds_internal(false); } void IsSubGeometry_Bounds_Image() { IsSubGeometry_Bounds_internal(true); } void IsSubGeometry_Bounds_internal(bool isImage) { auto newBounds = aDummyGeometry->GetBounds(); newBounds[0] = 10; newBounds[1] = 20; newBounds[2] = 10; newBounds[3] = 20; newBounds[4] = 10; newBounds[5] = 20; aDummyGeometry->SetBounds(newBounds); aDummyGeometry->SetImageGeometry(isImage); CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); for (unsigned int i = 0; i < 6; ++i) { auto legalBounds = newBounds; if (i % 2 == 0) { legalBounds[i] += 1; } else { legalBounds[i] -= 1; } auto legalGeometry = aDummyGeometry->Clone(); legalGeometry->SetBounds(legalBounds); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } for (unsigned int i = 0; i < 6; ++i) { auto wrongBounds = aDummyGeometry->GetBounds(); if (i % 2 == 0) { wrongBounds[i] -= 1; } else { wrongBounds[i] += 1; } auto wrongGeometry = aDummyGeometry->Clone(); wrongGeometry->SetBounds(wrongBounds); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } void IsSubGeometry_Grid_Image() { IsSubGeometry_Grid_internal(true); } void IsSubGeometry_Grid_NoneImage() { IsSubGeometry_Grid_internal(false); } void IsSubGeometry_Grid_internal(bool isImage) { auto newBounds = aDummyGeometry->GetBounds(); newBounds[0] = 0; newBounds[1] = 20; newBounds[2] = 0; newBounds[3] = 20; newBounds[4] = 0; newBounds[5] = 20; aDummyGeometry->SetBounds(newBounds); aDummyGeometry->SetImageGeometry(isImage); auto smallerGeometry = aDummyGeometry->Clone(); newBounds[0] = 5; newBounds[1] = 10; newBounds[2] = 5; newBounds[3] = 10; newBounds[4] = 5; newBounds[5] = 10; smallerGeometry->SetBounds(newBounds); //legal negative shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] -= smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } //legal positive shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); legalOrigin[i] += smallerGeometry->GetSpacing()[i]; auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); } //wrong negative shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] -= 2 * mitk::eps; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } //wrong positive shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] += 2 * mitk::eps; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); } } void IsSubGeometry_Bounds_Oblique_NoneImage() { IsSubGeometry_Bounds_Oblique_internal(false); } void IsSubGeometry_Bounds_Oblique_Image() { IsSubGeometry_Bounds_Oblique_internal(true); } void IsSubGeometry_Bounds_Oblique_internal(bool isImage) { auto newBounds = aDummyGeometryOblique->GetBounds(); aDummyGeometryOblique->SetImageGeometry(isImage); //REMARK: used NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION to compensate rounding errors that //are interoduced when transforming points/indeces due to the oblique geometry. CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometryOblique, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); for (unsigned int i = 0; i < 6; ++i) { auto legalBounds = newBounds; if (i % 2 == 0) { legalBounds[i] += 1; } else { legalBounds[i] -= 1; } auto legalGeometry = aDummyGeometryOblique->Clone(); legalGeometry->SetBounds(legalBounds); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } for (unsigned int i = 0; i < 6; ++i) { auto wrongBounds = newBounds; if (i % 2 == 0) { wrongBounds[i] -= 1; } else { wrongBounds[i] += 1; } auto wrongGeometry = aDummyGeometryOblique->Clone(); wrongGeometry->SetBounds(wrongBounds); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } } void IsSubGeometry_Grid_Oblique_NoneImage() { IsSubGeometry_Grid_Oblique_internal(false); } void IsSubGeometry_Grid_Oblique_Image() { IsSubGeometry_Grid_Oblique_internal(true); } void IsSubGeometry_Grid_Oblique_internal(bool isImage) { auto newBounds = aDummyGeometryOblique->GetBounds(); newBounds[0] = 0; newBounds[1] = 20; newBounds[2] = 0; newBounds[3] = 20; newBounds[4] = 0; newBounds[5] = 20; aDummyGeometryOblique->SetBounds(newBounds); aDummyGeometryOblique->SetImageGeometry(isImage); auto smallerGeometry = aDummyGeometryOblique->Clone(); newBounds[0] = 5; newBounds[1] = 10; newBounds[2] = 5; newBounds[3] = 10; newBounds[4] = 5; newBounds[5] = 10; smallerGeometry->SetBounds(newBounds); //REMARK: used NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION in the following checks //to compensate rounding errors that are interoduced when transforming points/indeces //due to the oblique geometry. //legal negative shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); mitk::Point3D index; smallerGeometry->WorldToIndex(legalOrigin, index); index[i] -= 1; smallerGeometry->IndexToWorld(index, legalOrigin); auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } //legal positive shift for (unsigned int i = 0; i < 3; ++i) { auto legalOrigin = smallerGeometry->GetOrigin(); mitk::Point3D index; smallerGeometry->WorldToIndex(legalOrigin, index); index[i] += 1; smallerGeometry->IndexToWorld(index, legalOrigin); auto legalGeometry = smallerGeometry->Clone(); legalGeometry->SetOrigin(legalOrigin); CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } //wrong negative shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] -= 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } //wrong positive shift for (unsigned int i = 0; i < 3; ++i) { auto wrongOrigin = smallerGeometry->GetOrigin(); wrongOrigin[i] += 2 * mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION; auto wrongGeometry = smallerGeometry->Clone(); wrongGeometry->SetOrigin(wrongOrigin); CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometryOblique, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, mitk::NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, true)); } } }; // end class mitkBaseGeometryTestSuite MITK_TEST_SUITE_REGISTRATION(mitkBaseGeometry) diff --git a/Modules/Core/test/mitkIOUtilTest.cpp b/Modules/Core/test/mitkIOUtilTest.cpp index 31fadfb024..eaad746e4a 100644 --- a/Modules/Core/test/mitkIOUtilTest.cpp +++ b/Modules/Core/test/mitkIOUtilTest.cpp @@ -1,311 +1,312 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTestingMacros.h" #include #include #include +#include #include #include #include #include #include #include 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); MITK_TEST(TestUtf8); 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())); + CPPUNIT_ASSERT(itksys::SystemTools::RemoveADirectory(mitk::Utf8Util::Local8BitToUtf8(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())); + CPPUNIT_ASSERT(itksys::SystemTools::RemoveADirectory(mitk::Utf8Util::Local8BitToUtf8(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()); } void TestUtf8() { const std::string utf8Path = u8"UTF-8/\u00c4.nrrd"; // LATIN CAPITAL LETTER A WITH DIAERESIS (U+00C4) - const std::string local8BitPath = mitk::IOUtil::Utf8ToLocal8Bit(utf8Path); + const std::string local8BitPath = mitk::Utf8Util::Utf8ToLocal8Bit(utf8Path); - CPPUNIT_ASSERT(utf8Path == mitk::IOUtil::Local8BitToUtf8(local8BitPath)); + CPPUNIT_ASSERT(utf8Path == mitk::Utf8Util::Local8BitToUtf8(local8BitPath)); const std::string path = GetTestDataFilePath(local8BitPath); std::fstream file; file.open(path); CPPUNIT_ASSERT(file.is_open()); auto image = mitk::IOUtil::Load(path); CPPUNIT_ASSERT(image.IsNotNull()); } }; MITK_TEST_SUITE_REGISTRATION(mitkIOUtil) diff --git a/Modules/Core/test/mitkImageAccessorTest.cpp b/Modules/Core/test/mitkImageAccessorTest.cpp index e85fb2a72c..4441ac2e42 100644 --- a/Modules/Core/test/mitkImageAccessorTest.cpp +++ b/Modules/Core/test/mitkImageAccessorTest.cpp @@ -1,247 +1,234 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#include "itkBarrier.h" #include "mitkIOUtil.h" #include "mitkImage.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImagePixelWriteAccessor.h" #include "mitkImageReadAccessor.h" #include "mitkImageTimeSelector.h" #include "mitkImageWriteAccessor.h" #include -#include #include #include #include #include +#include +#include struct ThreadData { - itk::Barrier::Pointer m_Barrier; // holds a pointer to the used barrier - mitk::Image::Pointer data; // some random data - int m_NoOfThreads; // holds the number of generated threads - bool m_Successful; // to check if everything worked -}; - -itk::SimpleFastMutexLock testMutex; - -ITK_THREAD_RETURN_TYPE ThreadMethod(void *data) -{ - /* extract data pointer from Thread Info structure */ - auto *pInfo = (struct itk::MultiThreader::ThreadInfoStruct *)data; - - // some data validity checking - if (pInfo == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) + ThreadData() + : Successful(true), + RandGen(std::random_device()()) { - return ITK_THREAD_RETURN_VALUE; } - // obtain user data for processing - auto *threadData = (ThreadData *)pInfo->UserData; + mitk::Image::Pointer Data; // some random data + bool Successful; // to check if everything worked + std::mt19937 RandGen; + std::mutex RandGenMutex; +}; - srand(pInfo->ThreadID); +std::mutex testMutex; - mitk::Image::Pointer im = threadData->data; +itk::ITK_THREAD_RETURN_TYPE ThreadMethod(ThreadData *threadData) +{ + if (nullptr == threadData) + return itk::ITK_THREAD_RETURN_DEFAULT_VALUE; + + mitk::Image::Pointer im = threadData->Data; int nrSlices = im->GetDimension(2); + std::uniform_int_distribution<> distrib(1, 1000000); + // Create randomly a PixelRead- or PixelWriteAccessor for a slice and access all pixels in it. try { - if (rand() % 2) + threadData->RandGenMutex.lock(); + auto even = distrib(threadData->RandGen) % 2; + auto slice = distrib(threadData->RandGen) % nrSlices; + threadData->RandGenMutex.unlock(); + + if (even) { - testMutex.Lock(); - mitk::ImageDataItem *iDi = im->GetSliceData(rand() % nrSlices); - testMutex.Unlock(); + testMutex.lock(); + mitk::ImageDataItem *iDi = im->GetSliceData(slice); + testMutex.unlock(); while (!iDi->IsComplete()) { } // MITK_INFO << "pixeltype: " << im->GetPixelType().GetComponentTypeAsString(); if ((im->GetPixelType().GetComponentTypeAsString() == "short") && (im->GetDimension() == 3)) { // Use pixeltype&dimension specific read accessor int xlength = im->GetDimension(0); int ylength = im->GetDimension(1); mitk::ImagePixelReadAccessor readAccessor(im, iDi); itk::Index<2> idx; for (int i = 0; i < xlength; ++i) { for (int j = 0; j < ylength; ++j) { idx[0] = i; idx[1] = j; readAccessor.GetPixelByIndexSafe(idx); } } } else { // use general accessor mitk::ImageReadAccessor *iRA = new mitk::ImageReadAccessor(im, iDi); delete iRA; } } else { - testMutex.Lock(); - mitk::ImageDataItem *iDi = im->GetSliceData(rand() % nrSlices); - testMutex.Unlock(); + testMutex.lock(); + mitk::ImageDataItem *iDi = im->GetSliceData(slice); + testMutex.unlock(); while (!iDi->IsComplete()) { } if ((im->GetPixelType().GetComponentTypeAsString() == "short") && (im->GetDimension() == 3)) { // Use pixeltype&dimension specific read accessor int xlength = im->GetDimension(0); int ylength = im->GetDimension(1); mitk::ImagePixelWriteAccessor writeAccessor(im, iDi); itk::Index<2> idx; for (int i = 0; i < xlength; ++i) { for (int j = 0; j < ylength; ++j) { idx[0] = i; idx[1] = j; - short newVal = rand() % 16000; + threadData->RandGenMutex.lock(); + short newVal = distrib(threadData->RandGen) % 16000; + threadData->RandGenMutex.unlock(); writeAccessor.SetPixelByIndexSafe(idx, newVal); short val = writeAccessor.GetPixelByIndexSafe(idx); if (val != newVal) { - threadData->m_Successful = false; + threadData->Successful = false; } } } } else { // use general accessor mitk::ImageWriteAccessor iB(im, iDi); void *pointer = iB.GetData(); *((char *)pointer) = 0; } } } catch ( const mitk::MemoryIsLockedException &e ) { - threadData->m_Successful = false; + threadData->Successful = false; e.Print(std::cout); } catch ( const mitk::Exception &e ) { - threadData->m_Successful = false; + threadData->Successful = false; e.Print(std::cout); } - // data processing end! - threadData->m_Barrier->Wait(); - return ITK_THREAD_RETURN_VALUE; + return itk::ITK_THREAD_RETURN_DEFAULT_VALUE; } int mitkImageAccessorTest(int argc, char *argv[]) { MITK_TEST_BEGIN("mitkImageAccessorTest"); std::cout << "Loading file: "; if (argc == 0) { std::cout << "no file specified [FAILED]" << std::endl; return EXIT_FAILURE; } mitk::Image::Pointer image = nullptr; try { image = mitk::IOUtil::Load(std::string(argv[1])); if (image.IsNull()) { MITK_TEST_FAILED_MSG(<< "file could not be loaded [FAILED]") } } catch ( const itk::ExceptionObject &ex ) { MITK_TEST_FAILED_MSG(<< "Exception: " << ex.GetDescription() << "[FAILED]") } // CHECK INAPPROPRIATE AND SPECIAL USAGE // recursive mutex lock MITK_TEST_OUTPUT(<< "Testing a recursive mutex lock attempt, should end in an exception ..."); MITK_TEST_FOR_EXCEPTION_BEGIN(mitk::Exception) mitk::ImageWriteAccessor first(image); mitk::ImageReadAccessor second(image); MITK_TEST_FOR_EXCEPTION_END(mitk::Exception) // ignore lock mechanism in read accessor try { mitk::ImageWriteAccessor first(image); mitk::ImageReadAccessor second(image, nullptr, mitk::ImageAccessorBase::IgnoreLock); MITK_TEST_CONDITION_REQUIRED(true, "Testing the option flag \"IgnoreLock\" in ReadAccessor"); } catch (const mitk::Exception & /*e*/) { MITK_TEST_CONDITION_REQUIRED(false, "Ignoring the lock mechanism leads to exception."); } // CREATE THREADS image->GetGeometry()->Initialize(); - itk::MultiThreader::Pointer threader = itk::MultiThreader::New(); - unsigned int noOfThreads = 100; + ThreadData threadData; + threadData.Data = image; - // initialize barrier - itk::Barrier::Pointer barrier = itk::Barrier::New(); - barrier->Initialize(noOfThreads + 1); // add one for we stop the base thread when the worker threads are processing - - auto *threadData = new ThreadData; - threadData->m_Barrier = barrier; - threadData->m_NoOfThreads = noOfThreads; - threadData->data = image; - threadData->m_Successful = true; + constexpr size_t noOfThreads = 100; + std::array threads; // spawn threads - for (unsigned int i = 0; i < noOfThreads; ++i) - { - threader->SpawnThread(ThreadMethod, threadData); - } - // stop the base thread during worker thread execution - barrier->Wait(); + for (size_t i = 0; i < noOfThreads; ++i) + threads[i] = std::thread(ThreadMethod, &threadData); // terminate threads - for (unsigned int j = 0; j < noOfThreads; ++j) + for (size_t i = 0; i < noOfThreads; ++i) { - threader->TerminateThread(j); + if (threads[i].joinable()) + threads[i].join(); } - bool TestSuccessful = threadData->m_Successful; - delete threadData; + bool TestSuccessful = threadData.Successful; MITK_TEST_CONDITION_REQUIRED(TestSuccessful, "Testing image access from multiple threads"); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkImageDimensionConverterTest.cpp b/Modules/Core/test/mitkImageDimensionConverterTest.cpp index 85330086ef..2428741336 100644 --- a/Modules/Core/test/mitkImageDimensionConverterTest.cpp +++ b/Modules/Core/test/mitkImageDimensionConverterTest.cpp @@ -1,260 +1,260 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 includes #include "mitkTestingConfig.h" #include #include #include #include #include #include #include #include #include #include // itk includes #include #include // stl includes #include // vtk includes #include int mitkImageDimensionConverterTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN(mitkImageDimensionConverterTest); // Define an epsilon which is the allowed error float eps = 0.00001; // Define helper variables float error = 0; bool matrixIsEqual = true; std::stringstream sstream; mitk::Convert2Dto3DImageFilter::Pointer convertFilter = mitk::Convert2Dto3DImageFilter::New(); /////////////////////////////////////// // Create 2D Image with a 3D rotation from scratch. typedef itk::Image ImageType; ImageType::Pointer itkImage = ImageType::New(); ImageType::RegionType myRegion; ImageType::SizeType mySize; ImageType::IndexType myIndex; ImageType::SpacingType mySpacing; mySpacing[0] = 1; mySpacing[1] = 1; myIndex[0] = 0; myIndex[1] = 0; mySize[0] = 50; mySize[1] = 50; myRegion.SetSize(mySize); myRegion.SetIndex(myIndex); itkImage->SetSpacing(mySpacing); itkImage->SetRegions(myRegion); itkImage->Allocate(); itkImage->FillBuffer(50); mitk::Image::Pointer mitkImage2D; mitk::CastToMitkImage(itkImage, mitkImage2D); // rotate mitk::Point3D p; p[0] = 1; p[1] = 3; p[2] = 5; mitk::Vector3D v; v[0] = 0.3; v[1] = 1; v[2] = 0.1; mitk::RotationOperation op(mitk::OpROTATE, p, v, 35); mitkImage2D->GetGeometry()->ExecuteOperation(&op); // Save original Geometry infos mitk::Vector3D Original_col0, Original_col1, Original_col2; Original_col0.SetVnlVector( - mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); + mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref()); Original_col1.SetVnlVector( - mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); + mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref()); Original_col2.SetVnlVector( - mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); + mitkImage2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref()); MITK_INFO << "Rotated Matrix: " << Original_col0 << " " << Original_col1 << " " << Original_col2; mitk::Point3D Original_Origin = mitkImage2D->GetGeometry()->GetOrigin(); mitk::Vector3D Original_Spacing = mitkImage2D->GetGeometry()->GetSpacing(); MITK_TEST_CONDITION_REQUIRED(mitkImage2D->GetDimension() == 2, "Created Image is Dimension 2"); /////////////////////////////////////// // mitkImage2D is now a 2D image with 3D Geometry information. // Save it without conversion and load it again. It should have an identitiy matrix sstream.clear(); sstream << MITK_TEST_OUTPUT_DIR << "" << "/rotatedImage2D.nrrd"; mitk::IOUtil::Save(mitkImage2D, sstream.str()); mitk::Image::Pointer imageLoaded2D = mitk::IOUtil::Load(sstream.str()); // check if image can be loaded MITK_TEST_CONDITION_REQUIRED(imageLoaded2D.IsNotNull(), "Loading saved 2D Image"); MITK_TEST_CONDITION_REQUIRED(imageLoaded2D->GetDimension() == 2, "Loaded Image is Dimension 2"); // check if spacing is ok mitk::Vector3D Loaded2D_Spacing = imageLoaded2D->GetGeometry()->GetSpacing(); error = std::fabs(Loaded2D_Spacing[0] - Original_Spacing[0]) + std::fabs(Loaded2D_Spacing[1] - Original_Spacing[1]) + std::fabs(Loaded2D_Spacing[2] - 1); MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Spacing"); // Check origin mitk::Point3D Loaded2D_Origin = imageLoaded2D->GetGeometry()->GetOrigin(); error = std::fabs(Loaded2D_Origin[0] - Original_Origin[0]) + std::fabs(Loaded2D_Origin[1] - Original_Origin[1]) + std::fabs(Loaded2D_Origin[2] - 0); MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Origin"); // Check matrix mitk::Vector3D Loaded2D_col0, Loaded2D_col1, Loaded2D_col2; Loaded2D_col0.SetVnlVector( - imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); + imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref()); Loaded2D_col1.SetVnlVector( - imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); + imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref()); Loaded2D_col2.SetVnlVector( - imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); + imageLoaded2D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref()); if ((std::fabs(1 - Loaded2D_col0[0]) > eps) || (std::fabs(0 - Loaded2D_col0[1]) > eps) || (std::fabs(0 - Loaded2D_col0[2]) > eps) || (std::fabs(0 - Loaded2D_col1[0]) > eps) || (std::fabs(1 - Loaded2D_col1[1]) > eps) || (std::fabs(0 - Loaded2D_col1[2]) > eps) || (std::fabs(0 - Loaded2D_col2[0]) > eps) || (std::fabs(0 - Loaded2D_col2[1]) > eps) || (std::fabs(1 - Loaded2D_col2[2]) > eps)) { matrixIsEqual = false; } else matrixIsEqual = true; MITK_TEST_CONDITION_REQUIRED(matrixIsEqual, "Compare Geometry: Matrix"); /////////////////////////////////////// // mitkImage2D is a 2D image with 3D Geometry information. // Convert it with filter to a 3D image and check if everything went well convertFilter->SetInput(mitkImage2D); convertFilter->Update(); mitk::Image::Pointer mitkImage3D = convertFilter->GetOutput(); MITK_TEST_CONDITION_REQUIRED(mitkImage3D->GetDimension() == 3, "Converted Image is Dimension 3"); // check if geometry is still same mitk::Vector3D Converted_Spacing = mitkImage3D->GetGeometry()->GetSpacing(); error = std::fabs(Converted_Spacing[0] - Original_Spacing[0]) + std::fabs(Converted_Spacing[1] - Original_Spacing[1]) + std::fabs(Converted_Spacing[2] - Original_Spacing[2]); MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Spacing"); mitk::Point3D Converted_Origin = mitkImage3D->GetGeometry()->GetOrigin(); error = std::fabs(Converted_Origin[0] - Original_Origin[0]) + std::fabs(Converted_Origin[1] - Original_Origin[1]) + std::fabs(Converted_Origin[2] - Original_Origin[2]); MITK_INFO << Converted_Origin << " and " << Original_Origin; MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Origin"); mitk::Vector3D Converted_col0, Converted_col1, Converted_col2; Converted_col0.SetVnlVector( - mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); + mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref()); Converted_col1.SetVnlVector( - mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); + mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref()); Converted_col2.SetVnlVector( - mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); + mitkImage3D->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref()); if ((std::fabs(Original_col0[0] - Converted_col0[0]) > eps) || (std::fabs(Original_col0[1] - Converted_col0[1]) > eps) || (std::fabs(Original_col0[2] - Converted_col0[2]) > eps) || (std::fabs(Original_col1[0] - Converted_col1[0]) > eps) || (std::fabs(Original_col1[1] - Converted_col1[1]) > eps) || (std::fabs(Original_col1[2] - Converted_col1[2]) > eps) || (std::fabs(Original_col2[0] - Converted_col2[0]) > eps) || (std::fabs(Original_col2[1] - Converted_col2[1]) > eps) || (std::fabs(Original_col2[2] - Converted_col2[2]) > eps)) { MITK_INFO << "Oh No! Original Image Matrix and Converted Image Matrix are different!"; MITK_INFO << "original Image:" << Original_col0 << " " << Original_col1 << " " << Original_col2; MITK_INFO << "converted Image:" << Converted_col0 << " " << Converted_col1 << " " << Converted_col2; matrixIsEqual = false; } else matrixIsEqual = true; MITK_TEST_CONDITION_REQUIRED(matrixIsEqual, "Compare Geometry: Matrix"); /////////////////////////////////////// // So far it seems good! now try to save and load the file std::stringstream sstream2; sstream2 << MITK_TEST_OUTPUT_DIR << "" << "/rotatedImage.nrrd"; mitk::IOUtil::Save(mitkImage3D, sstream2.str()); mitk::Image::Pointer imageLoaded = mitk::IOUtil::Load(sstream2.str()); // check if image can be loaded MITK_TEST_CONDITION_REQUIRED(imageLoaded.IsNotNull(), "Loading saved Image"); // check if loaded image is still what it should be: MITK_TEST_CONDITION_REQUIRED(imageLoaded->GetDimension() == 3, "Loaded Image is Dimension 3"); // check if geometry is still same mitk::Vector3D Loaded_Spacing = imageLoaded->GetGeometry()->GetSpacing(); error = std::fabs(Loaded_Spacing[0] - Original_Spacing[0]) + std::fabs(Loaded_Spacing[1] - Original_Spacing[1]) + std::fabs(Loaded_Spacing[2] - Original_Spacing[2]); MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Spacing"); mitk::Point3D Loaded_Origin = imageLoaded->GetGeometry()->GetOrigin(); error = std::fabs(Loaded_Origin[0] - Original_Origin[0]) + std::fabs(Loaded_Origin[1] - Original_Origin[1]) + std::fabs(Loaded_Origin[2] - Original_Origin[2]); MITK_TEST_CONDITION_REQUIRED(error < eps, "Compare Geometry: Origin"); mitk::Vector3D Loaded_col0, Loaded_col1, Loaded_col2; Loaded_col0.SetVnlVector( - imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); + imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref()); Loaded_col1.SetVnlVector( - imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); + imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref()); Loaded_col2.SetVnlVector( - imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); + imageLoaded->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref()); if ((std::fabs(Original_col0[0] - Loaded_col0[0]) > eps) || (std::fabs(Original_col0[1] - Loaded_col0[1]) > eps) || (std::fabs(Original_col0[2] - Loaded_col0[2]) > eps) || (std::fabs(Original_col1[0] - Loaded_col1[0]) > eps) || (std::fabs(Original_col1[1] - Loaded_col1[1]) > eps) || (std::fabs(Original_col1[2] - Loaded_col1[2]) > eps) || (std::fabs(Original_col2[0] - Loaded_col2[0]) > eps) || (std::fabs(Original_col2[1] - Loaded_col2[1]) > eps) || (std::fabs(Original_col2[2] - Loaded_col2[2]) > eps)) { MITK_INFO << "Oh No! Original Image Matrix and Converted Image Matrix are different!"; MITK_INFO << "original Image:" << Original_col0 << " " << Original_col1 << " " << Original_col2; MITK_INFO << "converted Image:" << Loaded_col0 << " " << Loaded_col1 << " " << Loaded_col2; matrixIsEqual = false; } else matrixIsEqual = true; MITK_TEST_CONDITION_REQUIRED(matrixIsEqual, "Compare Geometry: Matrix"); return 0; MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkImageGeneratorTest.cpp b/Modules/Core/test/mitkImageGeneratorTest.cpp index 57dc747de6..cc106d629a 100644 --- a/Modules/Core/test/mitkImageGeneratorTest.cpp +++ b/Modules/Core/test/mitkImageGeneratorTest.cpp @@ -1,327 +1,327 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include // MITK includes #include #include "mitkImage.h" #include "mitkImageGenerator.h" #include "mitkImageReadAccessor.h" #include "mitkImageStatisticsHolder.h" class mitkImageGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkImageGeneratorTestSuite); MITK_TEST(SetSpacingX2D_Success); MITK_TEST(SetDefaultSpacingX2D_Success); MITK_TEST(SetSpacingX3D_Success); MITK_TEST(SetSpacingY2D_Success); MITK_TEST(SetDefaultSpacingY2D_Success); MITK_TEST(SetSpacingY3D_Success); MITK_TEST(SetSpacingZ2D_Success); MITK_TEST(SetDefaultSpacingZ2D_Success); MITK_TEST(SetSpacingZ3D_Success); MITK_TEST(SetDimension2D_Success); MITK_TEST(SetDimension3D_Success); MITK_TEST(SetDimension4D_Success); MITK_TEST(SetDimensionX2D_Success); MITK_TEST(SetDimensionY2D_Success); MITK_TEST(SetDimensionZ3D_Success); MITK_TEST(SetDimensionT4D_Success); MITK_TEST(SetDimensions3Dc_Success); MITK_TEST(SetDataTypeFloat2D_Success); MITK_TEST(SetDataTypeUChar2D_Success); MITK_TEST(SetDataTypeInt3D_Success); MITK_TEST(SetDataTypeDouble3D_Success); MITK_TEST(SetDataTypeFloat4D_Success); MITK_TEST(SetDataTypeUChar4D_Success); MITK_TEST(SetDataTypeUInt3D_Success); MITK_TEST(SetPixelTypeFloat2D_Success); MITK_TEST(SetPixelTypeUChar2D_Success); MITK_TEST(SetPixelTypeInt3D_Success); MITK_TEST(SetPixelTypeDouble3D_Success); MITK_TEST(SetPixelTypeFloat4D_Success); MITK_TEST(SetPixelTypeUChar4D_Success); MITK_TEST(SetPixelTypeUInt3D_Success); MITK_TEST(MaxValueHolds_Success); MITK_TEST(MinValueHolds_Success); MITK_TEST(DefaultMaxValueHolds_Success); MITK_TEST(DefaultMinValueHolds_Success); MITK_TEST(SetGradientImageValues_Success); CPPUNIT_TEST_SUITE_END(); private: // create some images with arbitrary parameters (corner cases) mitk::Image::Pointer m_Image2Da; mitk::Image::Pointer m_Image2Db; mitk::Image::Pointer m_Image3Da; mitk::Image::Pointer m_Image3Db; mitk::Image::Pointer m_Image4Da; mitk::Image::Pointer m_Image4Db; mitk::Image::Pointer m_Image3Dc; public: void setUp() { m_Image2Da = mitk::ImageGenerator::GenerateRandomImage(2, 4, 0, 0, 0.1, 0.2, 0.3, 577, 23); m_Image2Db = mitk::ImageGenerator::GenerateRandomImage(1, 1, 0, 0); m_Image3Da = mitk::ImageGenerator::GenerateRandomImage(2, 4, 1, 0); m_Image3Db = mitk::ImageGenerator::GenerateRandomImage(2, 4, 8, 0); m_Image4Da = mitk::ImageGenerator::GenerateRandomImage(2, 4, 8, 1); m_Image4Db = mitk::ImageGenerator::GenerateRandomImage(2, 4, 8, 2); m_Image3Dc = mitk::ImageGenerator::GenerateGradientImage(1, 2, 3, 4, 5, 6); } void tearDown() { m_Image2Da = nullptr; m_Image2Db = nullptr; m_Image3Da = nullptr; m_Image3Db = nullptr; m_Image4Da = nullptr; m_Image4Db = nullptr; m_Image3Dc = nullptr; } void SetSpacingX2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing 2D x is set correctly.", fabs(m_Image2Da->GetGeometry()->GetSpacing()[0] - 0.1) < 0.0001); } void SetDefaultSpacingX2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if default spacing 2D x is set correctly.", fabs(m_Image2Db->GetGeometry()->GetSpacing()[0] - 1.0) < 0.0001); } void SetSpacingX3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing 3D x is set correctly.", fabs(m_Image3Dc->GetGeometry()->GetSpacing()[0] - 4) < 0.0001); } void SetSpacingY2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing 2D y is set correctly.", fabs(m_Image2Da->GetGeometry()->GetSpacing()[1] - 0.2) < 0.0001); } void SetDefaultSpacingY2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if default spacing 2D y is set correctly.", fabs(m_Image2Db->GetGeometry()->GetSpacing()[1] - 1.0) < 0.0001); } void SetSpacingY3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing 3D y is set correctly.", fabs(m_Image3Dc->GetGeometry()->GetSpacing()[1] - 5) < 0.0001); } void SetSpacingZ2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing 2D z is set correctly.", fabs(m_Image2Da->GetGeometry()->GetSpacing()[2] - 0.3) < 0.0001); } void SetDefaultSpacingZ2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if default spacing 2D z is set correctly.", fabs(m_Image2Db->GetGeometry()->GetSpacing()[2] - 1.0) < 0.0001); } void SetSpacingZ3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if spacing z is set correctly.", fabs(m_Image3Dc->GetGeometry()->GetSpacing()[2] - 6) < 0.0001); } void SetDimension2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 2D is set correctly.", m_Image2Da->GetDimension() == 2); CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 2D is set correctly.", m_Image2Db->GetDimension() == 2); } void SetDimension3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 3D is set correctly.",m_Image3Da->GetDimension() == 2); CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 3D is set correctly.", m_Image3Db->GetDimension() == 3); } void SetDimension4D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 4D is set correctly.", m_Image4Da->GetDimension() == 3); CPPUNIT_ASSERT_MESSAGE("Testing if the dimension 4D is set correctly.", m_Image4Db->GetDimension() == 4); } void SetDimensionX2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the X dimension of the 2D image is set correctly.", m_Image2Da->GetDimension(0) == 2); } void SetDimensionY2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the Y dimension of the 2D image is set correctly.", m_Image2Db->GetDimension(1) == 1); } void SetDimensionZ3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the Z dimension of the 3D image is set correctly.", m_Image3Da->GetDimension(2) == 1); CPPUNIT_ASSERT_MESSAGE("Testing if the Z dimension of the 3D image is set correctly.", m_Image3Db->GetDimension(2) == 8); } void SetDimensionT4D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the T dimension of the 4D image is set correctly.", m_Image4Da->GetDimension(3) == 1); CPPUNIT_ASSERT_MESSAGE("Testing if the T dimension of the 4D image is set correctly.", m_Image4Db->GetDimension(3) == 2); } void SetDimensions3Dc_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if image3Dc dimension x is set correctly.", m_Image3Dc->GetDimension(0) == 1); CPPUNIT_ASSERT_MESSAGE("Testing if image3Dc dimension y is set correctly.", m_Image3Dc->GetDimension(1) == 2); CPPUNIT_ASSERT_MESSAGE("Testing if image3Dc dimension z is set correctly.", m_Image3Dc->GetDimension(2) == 3); } void SetDataTypeFloat2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a float 3D image is set correctly.", - m_Image2Da->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT); + m_Image2Da->GetPixelType().GetComponentType() == itk::IOComponentEnum::FLOAT); } void SetDataTypeUChar2D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a UChar 2D image is set correctly.", - m_Image2Db->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR); + m_Image2Db->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR); } void SetDataTypeInt3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a Int 3D image is set correctly.", - m_Image3Da->GetPixelType().GetComponentType() == itk::ImageIOBase::INT); + m_Image3Da->GetPixelType().GetComponentType() == itk::IOComponentEnum::INT); } void SetDataTypeDouble3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a Double 3D image is set correctly.", - m_Image3Db->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE); + m_Image3Db->GetPixelType().GetComponentType() == itk::IOComponentEnum::DOUBLE); } void SetDataTypeFloat4D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a Float 4D image is set correctly.", - m_Image4Da->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT); + m_Image4Da->GetPixelType().GetComponentType() == itk::IOComponentEnum::FLOAT); } void SetDataTypeUChar4D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a UChar 4D image is set correctly.", - m_Image4Db->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR); + m_Image4Db->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR); } void SetDataTypeUInt3D_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if the data type for a UInt 3D image is set correctly.", - m_Image3Dc->GetPixelType().GetComponentType() == itk::ImageIOBase::UINT); + m_Image3Dc->GetPixelType().GetComponentType() == itk::IOComponentEnum::UINT); } void SetPixelTypeFloat2D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Float 2D image is set correctly.", m_Image2Da->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeUChar2D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a UChar 2D image is set correctly.", m_Image2Db->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeInt3D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Int 3D image is set correctly.", m_Image3Da->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeDouble3D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Double 3D image is set correctly.", m_Image3Db->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeFloat4D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a Float 4D image is set correctly.", m_Image4Da->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeUChar4D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a UChar 4D image is set correctly.", m_Image4Db->GetPixelType().GetPixelType() == scalarType); } void SetPixelTypeUInt3D_Success() { - itk::ImageIOBase::IOPixelType scalarType = itk::ImageIOBase::SCALAR; + auto scalarType = itk::IOPixelEnum::SCALAR; CPPUNIT_ASSERT_MESSAGE("Testing if the pixel type for a UInt 3D image is set correctly.", m_Image3Dc->GetPixelType().GetPixelType() == scalarType); } void MaxValueHolds_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if max value holds", m_Image2Da->GetStatistics()->GetScalarValueMax() <= 577); } void MinValueHolds_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if min value holds", m_Image2Da->GetStatistics()->GetScalarValueMin() >= 23); } void DefaultMaxValueHolds_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if default max value holds", m_Image3Da->GetStatistics()->GetScalarValueMax() <= 1000); } void DefaultMinValueHolds_Success() { CPPUNIT_ASSERT_MESSAGE("Testing if default min value holds", m_Image3Da->GetStatistics()->GetScalarValueMin() >= 0); } void SetGradientImageValues_Success() { const unsigned int *image3DcBuffer = nullptr; try { mitk::ImageReadAccessor readAccess(m_Image3Dc); image3DcBuffer = static_cast(readAccess.GetData()); } catch (...) { MITK_ERROR << "Read access not granted on mitk::Image."; } for (unsigned int i = 0; i < 2 * 3; i++) { CPPUNIT_ASSERT_MESSAGE("Testing if gradient image values are set correctly", image3DcBuffer[i] == i); } } }; MITK_TEST_SUITE_REGISTRATION(mitkImageGenerator) diff --git a/Modules/Core/test/mitkImportItkImageTest.cpp b/Modules/Core/test/mitkImportItkImageTest.cpp index 90afbe80f3..1ce29b55b1 100644 --- a/Modules/Core/test/mitkImportItkImageTest.cpp +++ b/Modules/Core/test/mitkImportItkImageTest.cpp @@ -1,306 +1,306 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkITKImageImport.h" #include "mitkImageCast.h" #include "mitkTestingMacros.h" #include "mitkImagePixelReadAccessor.h" #include #include /** * Create a test image with random pixel values. The image size is determined by the input parameter. * * @param size the number of voxels in each dimension */ template typename itk::Image::Pointer CreateTestImageRandom(short int size) { typedef typename itk::Image ImageType; itk::Size regionSize; regionSize.Fill(size); typename itk::RandomImageSource::Pointer randomImageSource = itk::RandomImageSource::New(); - randomImageSource->SetNumberOfThreads(1); // to produce non-random results + randomImageSource->SetNumberOfWorkUnits(1); // to produce non-random results randomImageSource->SetSize(regionSize); randomImageSource->Update(); return randomImageSource->GetOutput(); } /** * Create a test vector image (with two components) with a single pixel value. The image size is determined by the input * parameter. * * @param value the pixel value the created image is filled with * @param size the number of voxels in each dimension */ template typename itk::VectorImage::Pointer CreateTestVectorImageFixedValue( size_t size, const itk::VariableLengthVector &value) { typedef typename itk::VectorImage ImageType; typedef typename ImageType::Pointer ImagePointer; typename ImageType::RegionType imageRegion; typename ImageType::RegionType::SizeType regionSize; regionSize.Fill(size); typename ImageType::RegionType::IndexType regionIndex; regionIndex.Fill(0); imageRegion.SetSize(regionSize); imageRegion.SetIndex(regionIndex); typename ImageType::SpacingType imageSpacing; imageSpacing.Fill(1.0f); typename ImageType::PointType imageOrigin; imageOrigin.Fill(0.0f); ImagePointer itkImage = ImageType::New(); itkImage->SetVectorLength(value.GetNumberOfElements()); itkImage->SetRegions(imageRegion); itkImage->SetOrigin(imageOrigin); itkImage->SetSpacing(imageSpacing); itkImage->Allocate(); itkImage->FillBuffer(value); return itkImage; } /** * Create a test image with a single pixel value. The image size is determined by the input parameter. * * @param value the pixel value the created image is filled with * @param size the number of voxels in each dimension */ template typename itk::Image::Pointer CreateTestImageFixedValue(size_t size, TPixel value) { typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; typename ImageType::RegionType imageRegion; typename ImageType::RegionType::SizeType regionSize; regionSize.Fill(size); typename ImageType::RegionType::IndexType regionIndex; regionIndex.Fill(0); imageRegion.SetSize(regionSize); imageRegion.SetIndex(regionIndex); typename ImageType::SpacingType imageSpacing; imageSpacing.Fill(1.0f); typename ImageType::PointType imageOrigin; imageOrigin.Fill(0.0f); ImagePointer itkImage = ImageType::New(); itkImage->SetRegions(imageRegion); itkImage->SetOrigin(imageOrigin); itkImage->SetSpacing(imageSpacing); itkImage->Allocate(); itkImage->FillBuffer(value); return itkImage; } /** * Compares the meta information of both given images for equality. */ template bool Assert_ImageMetaData_AreEqual(typename ImageType::Pointer itkImage, mitk::Image::Pointer mitkImage) { bool return_value = true; typename ImageType::RegionType itkRegion = itkImage->GetLargestPossibleRegion(); typename ImageType::SizeType itkImageSize = itkRegion.GetSize(); // check dimension for (unsigned int idx = 0; idx < mitkImage->GetDimension(); idx++) { return_value &= (itkImageSize[idx] == mitkImage->GetDimension(idx)); } MITK_TEST_CONDITION(return_value, " - Dimensions equal!") // check pixel type bool ptype_compare = (mitkImage->GetPixelType() == mitk::MakePixelType()); return_value &= ptype_compare; MITK_TEST_CONDITION(ptype_compare, " - Pixel types equal!") mitk::BaseGeometry *imageGeometry = mitkImage->GetGeometry(); const mitk::Point3D origin = imageGeometry->GetOrigin(); bool origin_compare = true; for (unsigned int idx = 0; idx < 3; idx++) { origin_compare &= (itkImage->GetOrigin()[idx] == origin[idx]); } return_value &= origin_compare; MITK_TEST_CONDITION(origin_compare, " - Origin equals!") return return_value; } /** * Generates a random itk image and imports it to mitk image through ImportItkImage and compares the values * voxel-wise afterwards */ template void Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue() { std::stringstream msg; msg << "Current type: (Random Image, " << VDimension << "D):" << typeid(TPixel).name() << "\n"; std::cout << msg.str(); bool assert_value = true; typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; ImagePointer itkImage = CreateTestImageRandom(5); mitk::Image::Pointer output_import = mitk::ImportItkImage(itkImage); itk::ImageRegionConstIteratorWithIndex iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); mitk::ImagePixelReadAccessor readAccessor(output_import); bool difference = false; while (!iter.IsAtEnd()) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex(iter.GetIndex()); difference |= (ref != val); if (difference) { std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; } assert_value = Assert_ImageMetaData_AreEqual(itkImage, output_import); MITK_TEST_CONDITION(assert_value && (!difference), "Pixel values are same in voxel-wise comparison."); } /** * Generates an itk image with fixed pixel value and imports it to mitk image through ImportItkImage * and compares the values voxel-wise afterwards */ template void Assert_ItkImageImportSucceded_ReturnsTrue() { std::stringstream msg; msg << "Current type: " << VDimension << "D):" << typeid(TPixel).name() << "\n"; std::cout << msg.str(); bool assert_value = true; typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; ImagePointer itkImage = CreateTestImageFixedValue(5, itk::NumericTraits::min()); mitk::Image::Pointer output_import = mitk::ImportItkImage(itkImage); itk::ImageRegionConstIteratorWithIndex iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); mitk::ImagePixelReadAccessor readAccessor(output_import); bool difference = false; while (!iter.IsAtEnd()) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex(iter.GetIndex()); difference |= (ref != val); if (difference) { std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; } assert_value = Assert_ImageMetaData_AreEqual(itkImage, output_import); MITK_TEST_CONDITION(assert_value && (!difference), "Pixel values are same in voxel-wise comparison."); } void Assert_ItkVectorImageImportAndCast_ReturnsTrue() { typedef itk::VectorImage ImageType; ImageType::PixelType value; value.SetSize(2); value.SetElement(0, 1); value.SetElement(0, 2); ImageType::Pointer itkImage = CreateTestVectorImageFixedValue(5, value); mitk::Image::Pointer mitkImage = mitk::ImportItkImage(itkImage); mitk::PixelType pixelType = mitkImage->GetPixelType(); - MITK_TEST_CONDITION(pixelType.GetPixelType() == itk::ImageIOBase::VECTOR, "Vector image pixel type") - MITK_TEST_CONDITION(pixelType.GetComponentType() == itk::ImageIOBase::SHORT, "Vector image component type") + MITK_TEST_CONDITION(pixelType.GetPixelType() == itk::IOPixelEnum::VECTOR, "Vector image pixel type") + MITK_TEST_CONDITION(pixelType.GetComponentType() == itk::IOComponentEnum::SHORT, "Vector image component type") mitk::Image::Pointer mitkImage2; mitk::CastToMitkImage(itkImage, mitkImage2); mitk::PixelType pixelType2 = mitkImage2->GetPixelType(); MITK_TEST_CONDITION(pixelType == pixelType2, "ImportItkImage and CastToMitkImage produce same pixel types") ImageType::Pointer itkImageOut; mitk::CastToItkImage(mitkImage, itkImageOut); MITK_TEST_CONDITION(pixelType == mitk::MakePixelType(2), "MITK pixel type equals ITK pixel type") typedef itk::VectorImage IntImageType; IntImageType::Pointer itkIntImageOut; mitk::CastToItkImage(mitkImage, itkIntImageOut); MITK_TEST_CONDITION(!(pixelType == mitk::MakePixelType(2)), "MITK pixel type != ITK pixel type") mitk::Image::Pointer mitkImage3; mitk::CastToMitkImage(itkImageOut, mitkImage3); MITK_ASSERT_EQUAL(mitkImage, mitkImage3, "Equality for vector images"); } int mitkImportItkImageTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("mitkImportItkImageTest") Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on float"); Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on uchar"); Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on int"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on float"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on uchar"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on int"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on float"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on uchar"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on int"); Assert_ItkVectorImageImportAndCast_ReturnsTrue(); MITK_TEST_END() } diff --git a/Modules/Core/test/mitkItkImageIOTest.cpp b/Modules/Core/test/mitkItkImageIOTest.cpp index 1f608cc5f2..a9d8d6d9eb 100644 --- a/Modules/Core/test/mitkItkImageIOTest.cpp +++ b/Modules/Core/test/mitkItkImageIOTest.cpp @@ -1,427 +1,428 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkException.h" #include #include #include #include "mitkIOUtil.h" +#include #include "mitkITKImageImport.h" #include #include "itksys/SystemTools.hxx" #include #include #include #ifdef WIN32 #include "process.h" #else #include #endif class mitkItkImageIOTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkItkImageIOTestSuite); MITK_TEST(TestImageWriterJpg); MITK_TEST(TestImageWriterPng1); MITK_TEST(TestImageWriterPng2); MITK_TEST(TestImageWriterPng3); MITK_TEST(TestImageWriterSimple); MITK_TEST(TestWrite3DImageWithOnePlane); MITK_TEST(TestWrite3DImageWithTwoPlanes); MITK_TEST(TestWrite3DplusT_ArbitraryTG); MITK_TEST(TestWrite3DplusT_ProportionalTG); CPPUNIT_TEST_SUITE_END(); public: void setUp() override {} void tearDown() override {} void TestImageWriterJpg() { TestImageWriter("NrrdWritingTestImage.jpg"); } void TestImageWriterPng1() { TestImageWriter("Png2D-bw.png"); } void TestImageWriterPng2() { TestImageWriter("RenderingTestData/rgbImage.png"); } void TestImageWriterPng3() { TestImageWriter("RenderingTestData/rgbaImage.png"); } void TestWrite3DplusT_ArbitraryTG() { TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_arbitrary_time_geometry.nrrd"); } void TestWrite3DplusT_ProportionalTG() { TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_prop_time_geometry.nrrd"); } void TestImageWriterSimple() { // TODO } std::string AppendExtension(const std::string &filename, const char *extension) { std::string new_filename = filename; new_filename += extension; return new_filename; } bool CompareImageMetaData(mitk::Image::Pointer image, mitk::Image::Pointer reference, bool checkPixelType = true) { // switch to AreIdentical() methods as soon as Bug 11925 (Basic comparison operators) is fixed if (image->GetDimension() != reference->GetDimension()) { MITK_ERROR << "The image dimension differs: IN (" << image->GetDimension() << ") REF(" << reference->GetDimension() << ")"; return false; } // pixel type if (checkPixelType && (image->GetPixelType() != reference->GetPixelType() && image->GetPixelType().GetBitsPerComponent() != reference->GetPixelType().GetBitsPerComponent())) { MITK_ERROR << "Pixeltype differs ( image=" << image->GetPixelType().GetPixelTypeAsString() << "[" << image->GetPixelType().GetBitsPerComponent() << "]" << " reference=" << reference->GetPixelType().GetPixelTypeAsString() << "[" << reference->GetPixelType().GetBitsPerComponent() << "]" << " )"; return false; } return true; } /* Test writing picture formats like *.bmp, *.png, *.tiff or *.jpg NOTE: Saving as picture format must ignore PixelType comparison - not all bits per components are supported (see specification of the format) */ void TestPictureWriting(mitk::Image *image, const std::string &filename, const std::string &extension) { const std::string fullFileName = AppendExtension(filename, extension.c_str()); mitk::Image::Pointer singleSliceImage = nullptr; if (image->GetDimension() == 3) { mitk::ExtractSliceFilter::Pointer extractFilter = mitk::ExtractSliceFilter::New(); extractFilter->SetInput(image); extractFilter->SetWorldGeometry(image->GetSlicedGeometry()->GetPlaneGeometry(0)); extractFilter->Update(); singleSliceImage = extractFilter->GetOutput(); // test 3D writing in format supporting only 2D mitk::IOUtil::Save(image, fullFileName); // test images unsigned int foundImagesCount = 0; // if the image only contains one sinlge slice the itkImageSeriesWriter won't add a number like // filename.XX.extension if (image->GetDimension(2) == 1) { std::stringstream series_filenames; series_filenames << filename << extension; mitk::Image::Pointer compareImage = mitk::IOUtil::Load(series_filenames.str()); if (compareImage.IsNotNull()) { foundImagesCount++; MITK_TEST_CONDITION( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component } remove(series_filenames.str().c_str()); } else // test the whole slice stack { for (unsigned int i = 0; i < image->GetDimension(2); i++) { std::stringstream series_filenames; series_filenames << filename << "." << i + 1 << extension; mitk::Image::Pointer compareImage = mitk::IOUtil::Load(series_filenames.str()); if (compareImage.IsNotNull()) { foundImagesCount++; MITK_TEST_CONDITION( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component } remove(series_filenames.str().c_str()); } } MITK_TEST_CONDITION(foundImagesCount == image->GetDimension(2), "All 2D-Slices of a 3D image were stored correctly."); } else if (image->GetDimension() == 2) { singleSliceImage = image; } // test 2D writing if (singleSliceImage.IsNotNull()) { try { mitk::IOUtil::Save(singleSliceImage, fullFileName); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(fullFileName.c_str()); MITK_TEST_CONDITION_REQUIRED(compareImage.IsNotNull(), "Image stored was succesfully loaded again"); MITK_TEST_CONDITION_REQUIRED( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component remove(fullFileName.c_str()); } catch (itk::ExceptionObject &e) { MITK_TEST_FAILED_MSG(<< "Exception during file writing for ." << extension << ": " << e.what()); } } } /** * test for writing NRRDs */ void TestNRRDWriting(const mitk::Image *image) { CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.nrrd"); tmpStream.close(); try { mitk::IOUtil::Save(image, tmpFilePath); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(tmpFilePath); CPPUNIT_ASSERT_MESSAGE("Image stored in NRRD format was succesfully loaded again", compareImage.IsNotNull()); /*It would make sence to check the images as well (see commented cppunit assert), but currently there seems to be a problem (exception) with most of the test images (partly it seems to be a problem when try to access the pixel content by AccessByItk_1 in mitk::CompareImageDataFilter. This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */ // CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE( "TimeGeometries are equal.", mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), mitk::eps, true)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", image->GetUID(), compareImage->GetUID()); remove(tmpFilePath.c_str()); } catch (...) { std::remove(tmpFilePath.c_str()); CPPUNIT_FAIL("Exception during NRRD file writing"); } } /** * test for writing MHDs */ void TestMHDWriting(const mitk::Image *image) { CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mhd"); tmpStream.close(); std::string tmpFilePathWithoutExt = tmpFilePath.substr(0, tmpFilePath.size() - 4); try { mitk::IOUtil::Save(image, tmpFilePath); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(tmpFilePath); CPPUNIT_ASSERT_MESSAGE("Image stored in MHD format was succesfully loaded again! ", compareImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE(".mhd file exists", - itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".mhd").c_str())); + itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(tmpFilePathWithoutExt + ".mhd").c_str())); CPPUNIT_ASSERT_MESSAGE(".raw or .zraw exists", - itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".raw").c_str()) || - itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".zraw").c_str())); + itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(tmpFilePathWithoutExt + ".raw").c_str()) || + itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(tmpFilePathWithoutExt + ".zraw").c_str())); /*It would make sence to check the images as well (see commented cppunit assert), but currently there seems to be a problem (exception) with most of the test images (partly it seems to be a problem when try to access the pixel content by AccessByItk_1 in mitk::CompareImageDataFilter. This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */ // CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE("TimeGeometries are equal.", mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), 5e-4, true)); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", image->GetUID(), compareImage->GetUID()); // delete remove(tmpFilePath.c_str()); remove((tmpFilePathWithoutExt + ".raw").c_str()); remove((tmpFilePathWithoutExt + ".zraw").c_str()); } catch (...) { CPPUNIT_FAIL("Exception during.mhd file writing"); } } /** * test for "ImageWriter". * * argc and argv are the command line parameters which were passed to * the ADD_TEST command in the CMakeLists.txt file. For the automatic * tests, argv is either empty for the simple tests or contains the filename * of a test image for the image tests (see CMakeLists.txt). */ void TestImageWriter(std::string sourcefile) { sourcefile = GetTestDataFilePath(sourcefile); // load image - CPPUNIT_ASSERT_MESSAGE("Checking whether source image exists", itksys::SystemTools::FileExists(sourcefile.c_str())); + CPPUNIT_ASSERT_MESSAGE("Checking whether source image exists", itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(sourcefile).c_str())); mitk::Image::Pointer image = nullptr; try { image = mitk::IOUtil::Load(sourcefile); } catch (...) { CPPUNIT_FAIL("Exception during file loading:"); } CPPUNIT_ASSERT_MESSAGE("loaded image not nullptr", image.IsNotNull()); // write ITK .mhd image (2D and 3D only) if (image->GetDimension() <= 3) { TestMHDWriting(image); } // testing more component image writing as nrrd files TestNRRDWriting(image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX"); tmpStream.close(); TestPictureWriting(image, tmpFilePath, ".png"); TestPictureWriting(image, tmpFilePath, ".jpg"); TestPictureWriting(image, tmpFilePath, ".tiff"); TestPictureWriting(image, tmpFilePath, ".bmp"); // always end with this! } /** * Try to write a 3D image with only one plane (a 2D images in disguise for all intents and purposes) */ void TestWrite3DImageWithOnePlane() { typedef itk::Image ImageType; ImageType::Pointer itkImage = ImageType::New(); ImageType::IndexType start; start.Fill(0); ImageType::SizeType size; size[0] = 100; size[1] = 100; size[2] = 1; ImageType::RegionType region; region.SetSize(size); region.SetIndex(start); itkImage->SetRegions(region); itkImage->Allocate(); itkImage->FillBuffer(0); itk::ImageRegionIterator imageIterator(itkImage, itkImage->GetLargestPossibleRegion()); // Make two squares while (!imageIterator.IsAtEnd()) { if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) && (imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20)) { imageIterator.Set(255); } if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) && (imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70)) { imageIterator.Set(60); } ++imageIterator; } mitk::Image::Pointer image = mitk::ImportItkImage(itkImage); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd")); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png")); } /** * Try to write a 3D image with only one plane (a 2D images in disguise for all intents and purposes) */ void TestWrite3DImageWithTwoPlanes() { typedef itk::Image ImageType; ImageType::Pointer itkImage = ImageType::New(); ImageType::IndexType start; start.Fill(0); ImageType::SizeType size; size[0] = 100; size[1] = 100; size[2] = 2; ImageType::RegionType region; region.SetSize(size); region.SetIndex(start); itkImage->SetRegions(region); itkImage->Allocate(); itkImage->FillBuffer(0); itk::ImageRegionIterator imageIterator(itkImage, itkImage->GetLargestPossibleRegion()); // Make two squares while (!imageIterator.IsAtEnd()) { if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) && (imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20)) { imageIterator.Set(255); } if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) && (imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70)) { imageIterator.Set(60); } ++imageIterator; } mitk::Image::Pointer image = mitk::ImportItkImage(itkImage); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd")); CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png")), mitk::Exception); } }; MITK_TEST_SUITE_REGISTRATION(mitkItkImageIO) diff --git a/Modules/Core/test/mitkLogTest.cpp b/Modules/Core/test/mitkLogTest.cpp index 119855e481..716d93c6cf 100644 --- a/Modules/Core/test/mitkLogTest.cpp +++ b/Modules/Core/test/mitkLogTest.cpp @@ -1,312 +1,300 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkCommon.h" #include "mitkTestingMacros.h" -#include #include #include #include #include +#include +#include /** Documentation * * @brief this class provides an accessible BackendCout to determine whether this backend was * used to process a message or not. * It is needed for the disable / enable backend test. */ class TestBackendCout : public mbilog::BackendCout { public: TestBackendCout() { m_Called = false; mbilog::BackendCout(); } void ProcessMessage(const mbilog::LogMessage &l) override { m_Called = true; mbilog::BackendCout::ProcessMessage(l); } bool WasCalled() { return m_Called; } private: bool m_Called; }; /** Documentation * * @brief Objects of this class can start an internal thread by calling the Start() method. * The thread is then logging messages until the method Stop() is called. The class * can be used to test if logging is thread-save by using multiple objects and let * them log simuntanously. */ class mitkTestLoggingThread : public itk::Object { public: mitkClassMacroItkParent(mitkTestLoggingThread, itk::Object); - mitkNewMacro1Param(mitkTestLoggingThread, itk::MultiThreader::Pointer); + itkFactorylessNewMacro(Self); int NumberOfMessages; protected: - mitkTestLoggingThread(itk::MultiThreader::Pointer MultiThreader) + mitkTestLoggingThread() + : NumberOfMessages(0), + LoggingRunning(true) { - ThreadID = -1; - NumberOfMessages = 0; - m_MultiThreader = MultiThreader; - LoggingRunning = true; } - bool LoggingRunning; - - int ThreadID; + ~mitkTestLoggingThread() + { + this->Stop(); + } - itk::MultiThreader::Pointer m_MultiThreader; + bool LoggingRunning; + std::thread Thread; void LogMessages() { + auto ThreadID = Thread.get_id(); + while (LoggingRunning) { MITK_INFO << "Test info stream in thread" << ThreadID << "\n even with newlines"; MITK_WARN << "Test warning stream in thread " << ThreadID << ". " << "Even with a very long text, even without meaning or implied meaning or content, just a long " "sentence to see whether something has problems with long sentences or output in files or into " "windows or commandlines or whatever."; MITK_DEBUG << "Test debugging stream in thread " << ThreadID; MITK_ERROR << "Test error stream in thread " << ThreadID; MITK_FATAL << "Test fatal stream in thread " << ThreadID; NumberOfMessages += 5; } } - static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void *pInfoStruct) + static void ThreadStartTracking(void *instance) { - /* extract this pointer from Thread Info structure */ - auto *pInfo = (struct itk::MultiThreader::ThreadInfoStruct *)pInfoStruct; - if (pInfo == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - auto *thisthread = (mitkTestLoggingThread *)pInfo->UserData; + auto* thisthread = reinterpret_cast(instance); if (thisthread != nullptr) thisthread->LogMessages(); - - return ITK_THREAD_RETURN_VALUE; } public: - int Start() + std::thread::id Start() { LoggingRunning = true; - this->ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); - return ThreadID; + Thread = std::thread(this->ThreadStartTracking, this); + return Thread.get_id(); } - void Stop() { LoggingRunning = false; } + void Stop() + { + LoggingRunning = false; + + if(Thread.joinable()) + Thread.join(); + } }; /** Documentation * * @brief This class holds static test methods to sturcture the test of the mitk logging mechanism. */ class mitkLogTestClass { public: static void TestSimpleLog() { bool testSucceded = true; try { MITK_INFO << "Test info stream."; MITK_WARN << "Test warning stream."; MITK_DEBUG << "Test debugging stream."; // only activated if cmake variable is on! // so no worries if you see no output for this line MITK_ERROR << "Test error stream."; MITK_FATAL << "Test fatal stream."; } catch (const mitk::Exception &) { testSucceded = false; } MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging streams."); } static void TestObjectInfoLogging() { bool testSucceded = true; try { int i = 123; float f = .32234; double d = 123123; std::string testString = "testString"; std::stringstream testStringStream; testStringStream << "test" << "String" << "Stream"; mitk::Point3D testMitkPoint; testMitkPoint.Fill(2); MITK_INFO << i; MITK_INFO << f; MITK_INFO << d; MITK_INFO << testString; MITK_INFO << testStringStream.str(); MITK_INFO << testMitkPoint; } catch (const mitk::Exception &) { testSucceded = false; } MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging of object information."); } static void TestThreadSaveLog(bool toFile) { bool testSucceded = true; try { if (toFile) { std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/testthreadlog.log"; - itksys::SystemTools::RemoveFile(filename.c_str()); // remove old file, we do not want to append to large files + itksys::SystemTools::RemoveFile(mitk::Utf8Util::Local8BitToUtf8(filename).c_str()); // remove old file, we do not want to append to large files mitk::LoggingBackend::SetLogFile(filename.c_str()); } unsigned int numberOfThreads = 20; unsigned int threadRuntimeInMilliseconds = 2000; - std::vector threadIDs; + std::vector threadIDs; std::vector threads; - itk::MultiThreader::Pointer multiThreader = itk::MultiThreader::New(); for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { // initialize threads... - mitkTestLoggingThread::Pointer newThread = mitkTestLoggingThread::New(multiThreader); - threads.push_back(newThread); + threads.push_back(mitkTestLoggingThread::New()); std::cout << "Created " << threadIdx << ". thread." << std::endl; } for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { // start them std::cout << "Start " << threadIdx << ". thread." << std::endl; threadIDs.push_back(threads[threadIdx]->Start()); std::cout << threadIdx << ". thread has ID " << threadIDs[threadIdx] << std::endl; } // wait for some time (milliseconds) itksys::SystemTools::Delay(threadRuntimeInMilliseconds); for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) { - // stop them + // stop them and wait for them to end std::cout << "Stop " << threadIdx << ". thread." << std::endl; threads[threadIdx]->Stop(); - } - - for (unsigned int threadIdx = 0; threadIdx < numberOfThreads; ++threadIdx) - { - // Wait for all threads to end - multiThreader->TerminateThread(threadIDs[threadIdx]); - std::cout << "Terminated " << threadIdx << ". thread (" << threads[threadIdx]->NumberOfMessages << " messages)." - << std::endl; + std::cout << "Terminated " << threadIdx << ". thread (" << threads[threadIdx]->NumberOfMessages << " messages)." << std::endl; } } catch (std::exception &e) { MITK_ERROR << "exception during 'TestThreadSaveLog': " << e.what(); testSucceded = false; } catch (...) { MITK_ERROR << "unknown exception during 'TestThreadSaveLog'"; testSucceded = false; } // if no error occured until now, everything is ok MITK_TEST_CONDITION_REQUIRED(testSucceded, "Test logging in different threads."); } static void TestLoggingToFile() { std::string filename = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + "/testlog.log"; mitk::LoggingBackend::SetLogFile(filename.c_str()); MITK_INFO << "Test logging to default filename: " << mitk::LoggingBackend::GetLogFile(); - MITK_TEST_CONDITION_REQUIRED(itksys::SystemTools::FileExists(filename.c_str()), "Testing if log file exists."); + MITK_TEST_CONDITION_REQUIRED(itksys::SystemTools::FileExists(mitk::Utf8Util::Local8BitToUtf8(filename).c_str()), "Testing if log file exists."); // TODO delete log file? } static void TestAddAndRemoveBackends() { mbilog::BackendCout myBackend = mbilog::BackendCout(); mbilog::RegisterBackend(&myBackend); MITK_INFO << "Test logging"; mbilog::UnregisterBackend(&myBackend); // if no error occured until now, everything is ok MITK_TEST_CONDITION_REQUIRED(true, "Test add/remove logging backend."); } static void TestDefaultBackend() { // not possible now, because we cannot unregister the mitk logging backend in the moment. If such a method is added // to mbilog utility one may add this test. } static void TestEnableDisableBackends() { TestBackendCout myCoutBackend = TestBackendCout(); mbilog::RegisterBackend(&myCoutBackend); mbilog::DisableBackends(mbilog::Console); MITK_INFO << "There should be no output!"; bool success = !myCoutBackend.WasCalled(); mbilog::EnableBackends(mbilog::Console); MITK_INFO << "Now there should be an output."; success &= myCoutBackend.WasCalled(); mbilog::UnregisterBackend(&myCoutBackend); MITK_TEST_CONDITION_REQUIRED(success, "Test disable / enable logging backends.") } }; int mitkLogTest(int /* argc */, char * /*argv*/ []) { // always start with this! MITK_TEST_BEGIN("Log") MITK_TEST_OUTPUT(<< "TESTING ALL LOGGING OUTPUTS, ERROR MESSAGES ARE ALSO TESTED AND NOT MEANING AN ERROR OCCURED!") mitkLogTestClass::TestSimpleLog(); mitkLogTestClass::TestObjectInfoLogging(); mitkLogTestClass::TestLoggingToFile(); mitkLogTestClass::TestAddAndRemoveBackends(); mitkLogTestClass::TestThreadSaveLog(false); // false = to console mitkLogTestClass::TestThreadSaveLog(true); // true = to file mitkLogTestClass::TestEnableDisableBackends(); // TODO actually test file somehow? // always end with this! MITK_TEST_END() } diff --git a/Modules/Core/test/mitkPixelTypeTest.cpp b/Modules/Core/test/mitkPixelTypeTest.cpp index 0df8ef5104..3bafd0bff5 100644 --- a/Modules/Core/test/mitkPixelTypeTest.cpp +++ b/Modules/Core/test/mitkPixelTypeTest.cpp @@ -1,241 +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. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include "mitkTestingMacros.h" // std includes #include // MITK includes #include "mitkPixelType.h" #include // ITK includes #include "itkImage.h" #include // VTK includes #include struct MyObscurePixelType { typedef float ValueType; static const size_t Length = 2; }; //## Documentation //## main testing method class mitkPixelTypeTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPixelTypeTestSuite); MITK_TEST(GetComponentType_Success); MITK_TEST(GetPixelTypeAsString_Success); MITK_TEST(GetBpePtype_Success); MITK_TEST(GetNumberOfComponentsPtype_Success); MITK_TEST(GetBitsPerComponentPtype_Success); MITK_TEST(GetBpeItkPtype_Success); MITK_TEST(GetNumberOfComponentsItkPtype_Success); MITK_TEST(GetBitsPerComponentItkPtype_Success); MITK_TEST(ConstructorWithBrackets_Success); MITK_TEST(InitializeWithEqualSign_Success); MITK_TEST(EqualityOperator_Success); MITK_TEST(NotEqualityOperator_Success); MITK_TEST(GetPixelTypeUnknown_Success); MITK_TEST(SetLengthCorrectly_Success); MITK_TEST(ValueTypeCorresponds_Success); MITK_TEST(ImageTypeTraitInt3D_Success); MITK_TEST(ImageTypeTraitShort2D_Success); MITK_TEST(ImageTypeTraitVectorShort3D_Success); MITK_TEST(ImageTypeTraitItkInt3D_Success); MITK_TEST(ImageTypeTraitItkShort2D_Success); MITK_TEST(ImageTypeTraitItkVectorShort3D_Success); CPPUNIT_TEST_SUITE_END(); private: typedef itk::Image, 3> ItkImageType; typedef itk::Image MyObscureImageType; typedef itk::VectorImage VectorImageType; typedef itk::Image> FixedVectorImageType; public: void setUp() override { } void tearDown() override { } void GetComponentType_Success() { mitk::PixelType ptype = mitk::MakePixelType(); MITK_INFO << "m_Ptype = mitk::MakePixelType();"; MITK_INFO << "m_ItkPtype = mitk::MakePixelType();\n with itk::Image, 3> ItkImageType"; - CPPUNIT_ASSERT_MESSAGE("GetComponentType()", ptype.GetComponentType() == itk::ImageIOBase::INT); + CPPUNIT_ASSERT_MESSAGE("GetComponentType()", ptype.GetComponentType() == itk::IOComponentEnum::INT); } void GetPixelTypeAsString_Success() { mitk::PixelType ptype = mitk::MakePixelType(); MITK_INFO << ptype.GetPixelTypeAsString(); MITK_INFO << typeid(ItkImageType).name(); } void GetBpePtype_Success() { mitk::PixelType ptype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[m_Ptype] GetBpe()", ptype.GetBpe() == 8 * sizeof(int) * 5); } void GetNumberOfComponentsPtype_Success() { mitk::PixelType ptype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[ptype]GetNumberOfComponents()", ptype.GetNumberOfComponents() == 5); } void GetBitsPerComponentPtype_Success() { mitk::PixelType ptype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[ptype]GetBitsPerComponent()", ptype.GetBitsPerComponent() == 8 * sizeof(int)); } void GetBpeItkPtype_Success() { mitk::PixelType itkPtype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[itkPType] GetBpe()", itkPtype.GetBpe() == 8 * sizeof(int) * 5); } void GetNumberOfComponentsItkPtype_Success() { mitk::PixelType itkPtype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[itkPType] GetNumberOfComponents()", itkPtype.GetNumberOfComponents() == 5); } void GetBitsPerComponentItkPtype_Success() { mitk::PixelType itkPtype = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("[itkPType] GetBitsPerComponent()", itkPtype.GetBitsPerComponent() == 8 * sizeof(int)); } void ConstructorWithBrackets_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype2(ptype); - CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)- GetComponentType()", ptype2.GetComponentType() == itk::ImageIOBase::INT); + CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)- GetComponentType()", ptype2.GetComponentType() == itk::IOComponentEnum::INT); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetPixelType(", ptype2.GetPixelType() == ptype.GetPixelType()); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetComponentType(", ptype2.GetComponentType() == ptype.GetComponentType()); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetBpe()", ptype2.GetBpe() == 8 * sizeof(int) * 5); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetNumberOfComponents()", ptype2.GetNumberOfComponents() == 5); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetBitsPerComponent()", ptype2.GetBitsPerComponent() == 8 * sizeof(int)); } void InitializeWithEqualSign_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype2 = ptype; - CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetComponentType()", ptype2.GetComponentType() == itk::ImageIOBase::INT); + CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetComponentType()", ptype2.GetComponentType() == itk::IOComponentEnum::INT); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetPixelType(", ptype2.GetPixelType() == ptype.GetPixelType()); CPPUNIT_ASSERT_MESSAGE("ptype2( ptype)-GetComponentType(", ptype2.GetComponentType() == ptype.GetComponentType()); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetBpe()", ptype2.GetBpe() == 8 * sizeof(int) * 5); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetNumberOfComponents()", ptype2.GetNumberOfComponents() == 5); CPPUNIT_ASSERT_MESSAGE("ptype2 = ptype- GetBitsPerComponent()", ptype2.GetBitsPerComponent() == 8 * sizeof(int)); } void EqualityOperator_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype2 = ptype; CPPUNIT_ASSERT_MESSAGE("operator ==", ptype == ptype2); } void NotEqualityOperator_Success() { mitk::PixelType ptype = mitk::MakePixelType(); mitk::PixelType ptype3 = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("operator !=", ptype != ptype3); } void GetPixelTypeUnknown_Success() { // test instantiation mitk::PixelType obscurePixelType = mitk::MakePixelType(); - CPPUNIT_ASSERT_MESSAGE("PixelTypeId is 'UNKNOWN' ", obscurePixelType.GetPixelType() == itk::ImageIOBase::UNKNOWNPIXELTYPE); + CPPUNIT_ASSERT_MESSAGE("PixelTypeId is 'UNKNOWN' ", obscurePixelType.GetPixelType() == itk::IOPixelEnum::UNKNOWNPIXELTYPE); } void SetLengthCorrectly_Success() { mitk::PixelType obscurePixelType = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("Lenght was set correctly", obscurePixelType.GetNumberOfComponents() == MyObscurePixelType::Length); } void ValueTypeCorresponds_Success() { mitk::PixelType obscurePixelType = mitk::MakePixelType(); CPPUNIT_ASSERT_MESSAGE("ValueType corresponds.", obscurePixelType.GetComponentType() == mitk::MapPixelComponentType::value); } void ImageTypeTraitInt3D_Success() { // test ImageTypeTrait traits class CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid int 3D equals ITK typeid", typeid(mitk::ImageTypeTrait::ImageType) == typeid(itk::Image)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait::IsVectorImage == false)); } void ImageTypeTraitShort2D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid short 2D equals ITK typeid", typeid(mitk::ImageTypeTrait, 3>::ImageType) == typeid(itk::Image, 3>)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait, 3>::IsVectorImage == false)); } void ImageTypeTraitVectorShort3D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid short 3D equals ITK typeid", typeid(mitk::ImageTypeTrait, 3>::ImageType) == typeid(itk::VectorImage)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is a vector image", (mitk::ImageTypeTrait, 3>::IsVectorImage == true)); } void ImageTypeTraitItkInt3D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK int 3D equals ITK typeid", typeid(mitk::ImageTypeTrait>::ImageType) == typeid(itk::Image)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait>::IsVectorImage == false)); } void ImageTypeTraitItkShort2D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK short 2D equals ITK typeid", typeid(mitk::ImageTypeTrait, 3>>::ImageType) == typeid(itk::Image, 3>)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is no vector image", (mitk::ImageTypeTrait, 3>>::IsVectorImage == false)); } void ImageTypeTraitItkVectorShort3D_Success() { CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait typeid ITK short 3D equals ITK typeid", typeid(mitk::ImageTypeTrait>::ImageType) == typeid(itk::VectorImage)); CPPUNIT_ASSERT_MESSAGE("ImageTypeTrait is a vector image", (mitk::ImageTypeTrait>::IsVectorImage == true)); } }; MITK_TEST_SUITE_REGISTRATION(mitkPixelType) diff --git a/Modules/Core/test/mitkPlanePositionManagerTest.cpp b/Modules/Core/test/mitkPlanePositionManagerTest.cpp index 39197042d5..dbb23f8049 100644 --- a/Modules/Core/test/mitkPlanePositionManagerTest.cpp +++ b/Modules/Core/test/mitkPlanePositionManagerTest.cpp @@ -1,273 +1,272 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkBaseProperty.h" #include "mitkDataNode.h" #include "mitkGeometry3D.h" #include "mitkImage.h" #include "mitkInteractionConst.h" #include "mitkPlaneGeometry.h" #include "mitkPlanePositionManager.h" #include "mitkRotationOperation.h" #include "mitkSliceNavigationController.h" #include "mitkStandaloneDataStorage.h" #include "mitkStringProperty.h" #include "mitkSurface.h" #include "mitkTestingMacros.h" #include "usGetModuleContext.h" #include "usModuleContext.h" #include "usServiceReference.h" #include "vnl/vnl_vector.h" -#include std::vector m_Geometries; std::vector m_SliceIndices; mitk::PlanePositionManagerService *m_Service; int SetUpBeforeTest() { // Getting Service us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); m_Service = us::GetModuleContext()->GetService(serviceRef); if (m_Service == nullptr) return EXIT_FAILURE; // Creating different Geometries m_Geometries.reserve(100); mitk::PlaneGeometry::PlaneOrientation views[] = { mitk::PlaneGeometry::Axial, mitk::PlaneGeometry::Sagittal, mitk::PlaneGeometry::Frontal}; for (unsigned int i = 0; i < 100; ++i) { mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); mitk::ScalarType width = 256 + (0.01 * i); mitk::ScalarType height = 256 + (0.002 * i); mitk::Vector3D right; mitk::Vector3D down; right[0] = 1; right[1] = i; right[2] = 0.5; down[0] = i * 0.02; down[1] = 1; down[2] = i * 0.03; mitk::Vector3D spacing; mitk::FillVector3D(spacing, 1.0 * 0.02 * i, 1.0 * 0.15 * i, 1.0); mitk::Vector3D rightVector; mitk::FillVector3D(rightVector, 0.02 * (i + 1), 0 + (0.05 * i), 1.0); mitk::Vector3D downVector; mitk::FillVector3D(downVector, 1, 3 - 0.01 * i, 0.0345 * i); vnl_vector normal = vnl_cross_3d(rightVector.GetVnlVector(), downVector.GetVnlVector()); normal.normalize(); normal *= 1.5; mitk::Vector3D origin; origin.Fill(1); origin[0] = 12 + 0.03 * i; mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); mitk::Matrix3D matrix; matrix.GetVnlMatrix().set_column(0, rightVector.GetVnlVector()); matrix.GetVnlMatrix().set_column(1, downVector.GetVnlVector()); matrix.GetVnlMatrix().set_column(2, normal); transform->SetMatrix(matrix); transform->SetOffset(origin); plane->InitializeStandardPlane(width, height, transform, views[i % 3], i, true, false); m_Geometries.push_back(plane); } return EXIT_SUCCESS; } int testAddPlanePosition() { MITK_TEST_OUTPUT(<< "Starting Test: ######### A d d P l a n e P o s i t i o n #########"); MITK_TEST_CONDITION(m_Service != nullptr, "Testing getting of PlanePositionManagerService"); unsigned int currentID(m_Service->AddNewPlanePosition(m_Geometries.at(0), 0)); bool error = ((m_Service->GetNumberOfPlanePositions() != 1) || (currentID != 0)); if (error) { MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == 1, "Checking for correct number of planepositions"); MITK_TEST_CONDITION(currentID == 0, "Testing for correct ID"); return EXIT_FAILURE; } // Adding new planes for (unsigned int i = 1; i < m_Geometries.size(); ++i) { unsigned int newID = m_Service->AddNewPlanePosition(m_Geometries.at(i), i); error = ((m_Service->GetNumberOfPlanePositions() != i + 1) || (newID != (currentID + 1))); if (error) { MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == i + 1, "Checking for correct number of planepositions"); MITK_TEST_CONDITION(newID == (currentID + 1), "Testing for correct ID"); MITK_TEST_OUTPUT(<< "New: " << newID << " Last: " << currentID); return EXIT_FAILURE; } currentID = newID; } unsigned int numberOfPlanePos = m_Service->GetNumberOfPlanePositions(); // Adding existing planes -> nothing should change for (unsigned int i = 0; i < (m_Geometries.size() - 1) * 0.5; ++i) { unsigned int newID = m_Service->AddNewPlanePosition(m_Geometries.at(i * 2), i * 2); error = ((m_Service->GetNumberOfPlanePositions() != numberOfPlanePos) || (newID != i * 2)); if (error) { MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == numberOfPlanePos, "Checking for correct number of planepositions"); MITK_TEST_CONDITION(newID == i * 2, "Testing for correct ID"); return EXIT_FAILURE; } } return EXIT_SUCCESS; } int testGetPlanePosition() { bool error(true); MITK_TEST_OUTPUT(<< "Starting Test: ######### G e t P l a n e P o s i t i o n #########"); // Testing for existing planepositions for (unsigned int i = 0; i < m_Geometries.size(); ++i) { auto plane = m_Geometries.at(i); auto op = m_Service->GetPlanePosition(i); error = (!mitk::Equal(op->GetHeight(), plane->GetExtent(1)) || !mitk::Equal(op->GetWidth(), plane->GetExtent(0)) || !mitk::Equal(op->GetSpacing(), plane->GetSpacing()) || !mitk::Equal(op->GetTransform()->GetOffset(), plane->GetIndexToWorldTransform()->GetOffset()) || !mitk::Equal(op->GetDirectionVector().GetVnlVector(), - plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).normalize()) || + plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).normalize().as_ref()) || !mitk::MatrixEqualElementWise(op->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix())); if (error) { MITK_TEST_OUTPUT(<< "Iteration: " << i) MITK_TEST_CONDITION( mitk::Equal(op->GetHeight(), plane->GetExtent(1)) && mitk::Equal(op->GetWidth(), plane->GetExtent(0)), "Checking for correct extent"); MITK_TEST_CONDITION(mitk::Equal(op->GetSpacing(), plane->GetSpacing()), "Checking for correct spacing"); MITK_TEST_CONDITION(mitk::Equal(op->GetTransform()->GetOffset(), plane->GetIndexToWorldTransform()->GetOffset()), "Checking for correct offset"); MITK_INFO << "Op: " << op->GetDirectionVector() << " plane: " << plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2) << "\n"; MITK_TEST_CONDITION(mitk::Equal(op->GetDirectionVector().GetVnlVector(), - plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)), + plane->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref()), "Checking for correct direction"); MITK_TEST_CONDITION( mitk::MatrixEqualElementWise(op->GetTransform()->GetMatrix(), plane->GetIndexToWorldTransform()->GetMatrix()), "Checking for correct matrix"); return EXIT_FAILURE; } } // Testing for not existing planepositions error = (m_Service->GetPlanePosition(100000000) != nullptr || m_Service->GetPlanePosition(-1) != nullptr); if (error) { MITK_TEST_CONDITION(m_Service->GetPlanePosition(100000000) == nullptr, "Trying to get non existing pos"); MITK_TEST_CONDITION(m_Service->GetPlanePosition(-1) == nullptr, "Trying to get non existing pos"); return EXIT_FAILURE; } return EXIT_SUCCESS; } int testRemovePlanePosition() { MITK_TEST_OUTPUT(<< "Starting Test: ######### R e m o v e P l a n e P o s i t i o n #########"); unsigned int size = m_Service->GetNumberOfPlanePositions(); // Testing for invalid IDs bool removed = m_Service->RemovePlanePosition(-1); removed = m_Service->RemovePlanePosition(1000000); unsigned int size2 = m_Service->GetNumberOfPlanePositions(); if (removed) { MITK_TEST_CONDITION(removed == false, "Testing remove not existing planepositions"); MITK_TEST_CONDITION(size == size2, "Testing remove not existing planepositions"); return EXIT_FAILURE; } // Testing for valid IDs for (unsigned int i = 0; i < m_Geometries.size() * 0.5; i++) { removed = m_Service->RemovePlanePosition(i); unsigned int size2 = m_Service->GetNumberOfPlanePositions(); removed = (size2 == (size - (i + 1))); if (!removed) { MITK_TEST_CONDITION(removed == true, "Testing remove existing planepositions"); MITK_TEST_CONDITION(size == (size - i + 1), "Testing remove existing planepositions"); return EXIT_FAILURE; } } return EXIT_SUCCESS; } int testRemoveAll() { MITK_TEST_OUTPUT(<< "Starting Test: ######### R e m o v e A l l #########"); unsigned int numPos = m_Service->GetNumberOfPlanePositions(); MITK_INFO << numPos; m_Service->RemoveAllPlanePositions(); bool error(true); error = (m_Service->GetNumberOfPlanePositions() != 0 || m_Service->GetPlanePosition(60) != nullptr); if (error) { MITK_TEST_CONDITION(m_Service->GetNumberOfPlanePositions() == 0, "Testing remove all pos"); MITK_TEST_CONDITION(m_Service->GetPlanePosition(60) == nullptr, "Testing remove all pos"); return EXIT_FAILURE; } return EXIT_SUCCESS; } int mitkPlanePositionManagerTest(int, char *[]) { MITK_TEST_BEGIN("PlanePositionManager"); SetUpBeforeTest(); int result; MITK_TEST_CONDITION_REQUIRED((result = testAddPlanePosition()) == EXIT_SUCCESS, ""); MITK_TEST_CONDITION_REQUIRED((result = testGetPlanePosition()) == EXIT_SUCCESS, ""); MITK_TEST_CONDITION_REQUIRED((result = testRemovePlanePosition()) == EXIT_SUCCESS, ""); MITK_TEST_CONDITION_REQUIRED((result = testRemoveAll()) == EXIT_SUCCESS, ""); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp index 2139114345..6229d1b1bf 100644 --- a/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp +++ b/Modules/Core/test/mitkSurfaceToImageFilterTest.cpp @@ -1,253 +1,253 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkSurfaceToImageFilter.h" #include "mitkTestFixture.h" #include class mitkSurfaceToImageFilterTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSurfaceToImageFilterTestSuite); MITK_TEST(test3DSurfaceValidOutput); MITK_TEST(test3DSurfaceCorrect); MITK_TEST(test3DSurfaceIn4DImage); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ mitk::Surface::Pointer m_Surface; public: /** * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used * members for a new test case. (If the members are not used in a test, the method does not need to be called). */ void setUp() override { m_Surface = mitk::IOUtil::Load(GetTestDataFilePath("ball.stl")); } void tearDown() override {} void test3DSurfaceValidOutput() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); mitk::Image::Pointer additionalInputImage = mitk::Image::New(); additionalInputImage->Initialize(mitk::MakeScalarPixelType(), *m_Surface->GetTimeGeometry()); // Arrange the filter surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); CPPUNIT_ASSERT_MESSAGE( "SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType", - surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR); + surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR); surfaceToImageFilter->SetUShortBinaryPixelType(true); surfaceToImageFilter->Update(); CPPUNIT_ASSERT_MESSAGE( "SurfaceToImageFilter_AnyInputImageAndModeSetToBinary_ResultIsImageWithUCHARPixelType", - surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT); + surfaceToImageFilter->GetOutput()->GetPixelType().GetComponentType() == itk::IOComponentEnum::USHORT); } void test3DSurfaceCorrect() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); // todo I don't know if this image is always needed. There is no documentation of the filter. Use git blame and ask // the author. mitk::Image::Pointer additionalInputImage = mitk::Image::New(); auto *dims = new unsigned int[3]; dims[0] = 32; dims[1] = 32; dims[2] = 32; additionalInputImage->Initialize(mitk::MakeScalarPixelType(), 3, dims); additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform()); // Arrange the filter // The docu does not really tell if this is always needed. Could we skip SetImage in any case? surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); mitk::ImagePixelReadAccessor outputReader(surfaceToImageFilter->GetOutput()); itk::Index<3> idx; bool valuesCorrect = true; // Values outside the ball should be 0 idx[0] = 0; idx[1] = 0, idx[2] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 0; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 0, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 5; idx[1] = 9, idx[2] = 23; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); // Values inside the ball should be 1 idx[0] = 15; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 31; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 2; idx[1] = 15, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 15, idx[2] = 2; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 2, idx[2] = 15; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 6; idx[1] = 9, idx[2] = 23; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_OutputCorrect", valuesCorrect == true); } void test3DSurfaceIn4DImage() { mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); mitk::Image::Pointer additionalInputImage = mitk::Image::New(); auto *dims = new unsigned int[4]; dims[0] = 32; dims[1] = 32; dims[2] = 32; dims[3] = 2; additionalInputImage->Initialize(mitk::MakeScalarPixelType(), 4, dims); additionalInputImage->SetOrigin(m_Surface->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry()->SetIndexToWorldTransform(m_Surface->GetGeometry()->GetIndexToWorldTransform()); mitk::Image::Pointer secondStep = additionalInputImage->Clone(); unsigned int size = sizeof(unsigned char); for (unsigned int i = 0; i < secondStep->GetDimension(); ++i) { size *= secondStep->GetDimension(i); } { mitk::ImageWriteAccessor accessor(secondStep); memset(accessor.GetData(), 1, size); } additionalInputImage->GetTimeGeometry()->Expand(2); additionalInputImage->GetGeometry(1)->SetSpacing(secondStep->GetGeometry()->GetSpacing()); additionalInputImage->GetGeometry(1)->SetOrigin(secondStep->GetGeometry()->GetOrigin()); additionalInputImage->GetGeometry(1)->SetIndexToWorldTransform( secondStep->GetGeometry()->GetIndexToWorldTransform()); { mitk::ImageReadAccessor readAccess(secondStep); additionalInputImage->SetImportVolume(readAccess.GetData(), 0); additionalInputImage->SetImportVolume(readAccess.GetData(), 1); } // Arrange the filter surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(m_Surface); surfaceToImageFilter->SetImage(additionalInputImage); surfaceToImageFilter->Update(); mitk::ImagePixelReadAccessor outputReader(surfaceToImageFilter->GetOutput()); itk::Index<4> idx; bool valuesCorrect = true; // Values outside the ball should be 0 idx[0] = 0; idx[1] = 0, idx[2] = 0; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 0; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 0; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 0, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 5; idx[1] = 9, idx[2] = 23; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); // Values inside the ball should be 1 hould be 1 idx[0] = 15; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 31; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 2; idx[1] = 15, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 15, idx[2] = 2; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 15; idx[1] = 2, idx[2] = 15; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); idx[0] = 6; idx[1] = 9, idx[2] = 23; idx[3] = 0; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 1); // Values inside the ball but in the second timestep hould be 0 idx[0] = 15; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 31; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 2; idx[1] = 15, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 15, idx[2] = 2; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 15; idx[1] = 2, idx[2] = 15; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); idx[0] = 6; idx[1] = 9, idx[2] = 23; idx[3] = 1; valuesCorrect = valuesCorrect && (outputReader.GetPixelByIndex(idx) == 0); CPPUNIT_ASSERT_MESSAGE("SurfaceToImageFilter_BallSurfaceAsInput_Output4DCorrect", valuesCorrect == true); } }; MITK_TEST_SUITE_REGISTRATION(mitkSurfaceToImageFilter) diff --git a/Modules/Core/test/mitkTinyXMLTest.cpp b/Modules/Core/test/mitkTinyXMLTest.cpp index 392ab4384d..db0d7104d2 100644 --- a/Modules/Core/test/mitkTinyXMLTest.cpp +++ b/Modules/Core/test/mitkTinyXMLTest.cpp @@ -1,162 +1,163 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include // std includes #include #include #include // MITK includes #include "mitkStringProperty.h" #include +#include // itksys #include // VTK includes #include // vnl includes #include class mitkTinyXMLTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkTinyXMLTestSuite); MITK_TEST(TestingFunctionSetupWorks_Success); MITK_TEST(TestingReadValueFromSetupDocument_Success); MITK_TEST(TestingReadOutValueWorks_Success); MITK_TEST(TestDoubleValueWriteOut_Success); MITK_TEST(TestDoubleValueWriteOutManyDecimalPlaces_Success); CPPUNIT_TEST_SUITE_END(); private: - const std::string m_Filename = itksys::SystemTools::GetCurrentWorkingDirectory() + "/TinyXMLTest.txt"; + const std::string m_Filename = mitk::Utf8Util::Utf8ToLocal8Bit(itksys::SystemTools::GetCurrentWorkingDirectory()) + "/TinyXMLTest.txt"; const std::string m_ElementToStoreAttributeName = "DoubleTest"; const std::string m_AttributeToStoreName = "CommaValue"; tinyxml2::XMLDocument m_Document; tinyxml2::XMLElement *m_DoubleTest; double calcPrecision(const unsigned int requiredDecimalPlaces) { return pow(10.0, -1.0 * ((double)requiredDecimalPlaces)); } bool Setup(double valueToWrite) { m_Document.Clear(); m_DoubleTest = nullptr; // 1. create simple document m_Document.InsertEndChild(m_Document.NewDeclaration()); auto *version = m_Document.NewElement("Version"); version->SetAttribute("Writer", __FILE__); version->SetAttribute("CVSRevision", "$Revision: 17055 $"); version->SetAttribute("FileVersion", 1); m_Document.InsertEndChild(version); // 2. store one element containing a double value with potentially many after comma digits. auto *vElement = m_Document.NewElement(m_ElementToStoreAttributeName.c_str()); vElement->SetAttribute(m_AttributeToStoreName.c_str(), valueToWrite); m_Document.InsertEndChild(vElement); // 3. store in file. auto err = m_Document.SaveFile(m_Filename.c_str()); return tinyxml2::XML_SUCCESS == err; } public: void setUp() override {} void tearDown() override {} void TestingFunctionSetupWorks_Success() { CPPUNIT_ASSERT_MESSAGE("Test if Setup correctly writes data to file", Setup(1.0)); } int readValueFromSetupDocument(double &readOutValue) { if (tinyxml2::XML_SUCCESS != m_Document.LoadFile(m_Filename.c_str())) { CPPUNIT_ASSERT_MESSAGE("Test Setup failed, could not open file", false); return tinyxml2::XML_NO_ATTRIBUTE; } else { m_DoubleTest = m_Document.FirstChildElement(m_ElementToStoreAttributeName.c_str()); return m_DoubleTest->QueryDoubleAttribute(m_AttributeToStoreName.c_str(), &readOutValue); } } void TestingReadValueFromSetupDocument_Success() { if (tinyxml2::XML_SUCCESS != m_Document.LoadFile(m_Filename.c_str())) { CPPUNIT_ASSERT_MESSAGE("Test Setup failed, could not open file", tinyxml2::XML_SUCCESS != m_Document.LoadFile(m_Filename.c_str())); } else { m_DoubleTest = m_Document.FirstChildElement(m_ElementToStoreAttributeName.c_str()); CPPUNIT_ASSERT_MESSAGE("Test Setup could open file", m_DoubleTest != nullptr); } } /** * this first test ensures we can correctly readout values from the * TinyXMLDocument. */ void TestingReadOutValueWorks_Success() { double readValue; CPPUNIT_ASSERT_MESSAGE("checking if readout mechanism works.", tinyxml2::XML_SUCCESS == readValueFromSetupDocument(readValue)); } void TestDoubleValueWriteOut_Success() { const double valueToWrite = -1.123456; const int validDigitsAfterComma = 6; // indicates the number of valid digits after comma of valueToWrite const double neededPrecision = calcPrecision(validDigitsAfterComma + 1); double readValue; Setup(valueToWrite); readValueFromSetupDocument(readValue); CPPUNIT_ASSERT_MESSAGE("Testing if value valueToWrite equals readValue which was retrieved from TinyXML document", mitk::Equal(valueToWrite, readValue, neededPrecision)); } void TestDoubleValueWriteOutManyDecimalPlaces_Success() { const double valueToWrite = -1.12345678910111; const int validDigitsAfterComma = 14; // indicates the number of valid digits after comma of valueToWrite const double neededPrecision = calcPrecision(validDigitsAfterComma + 1); double readValue; Setup(valueToWrite); readValueFromSetupDocument(readValue); CPPUNIT_ASSERT_MESSAGE("Testing if value valueToWrite equals readValue which was retrieved from TinyXML document", mitk::Equal(valueToWrite, readValue, neededPrecision)); } }; MITK_TEST_SUITE_REGISTRATION(mitkTinyXML) diff --git a/Modules/DICOM/CMakeLists.txt b/Modules/DICOM/CMakeLists.txt index ebff7f5798..36966c5338 100644 --- a/Modules/DICOM/CMakeLists.txt +++ b/Modules/DICOM/CMakeLists.txt @@ -1,9 +1,9 @@ MITK_CREATE_MODULE( DEPENDS MitkCore PACKAGE_DEPENDS - PUBLIC GDCM tinyxml2 + PUBLIC GDCM|MSFF tinyxml2 PRIVATE DCMTK ITK|IOGDCM ) add_subdirectory(test) add_subdirectory(autoload/DICOMImageIO) diff --git a/Modules/DICOM/autoload/DICOMImageIO/include/mitkDICOMTagsOfInterestService.h b/Modules/DICOM/autoload/DICOMImageIO/include/mitkDICOMTagsOfInterestService.h index 85544efeea..d3eb0094d8 100644 --- a/Modules/DICOM/autoload/DICOMImageIO/include/mitkDICOMTagsOfInterestService.h +++ b/Modules/DICOM/autoload/DICOMImageIO/include/mitkDICOMTagsOfInterestService.h @@ -1,64 +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 mitkDICOMTagsOfInterestService_h #define mitkDICOMTagsOfInterestService_h #include +#include #include #include #include -#include -#include - namespace mitk { /** * \ingroup MicroServices_Interfaces * \brief DICOM tags of interest service. * * This service allows you to manage the tags of interest (toi). * All registred toi will be extracted when loading dicom data and stored as properties in the corresponding * base data object. In addition the service can (if available) use IPropertyPersistance and IPropertyDescriptions * to ensure that the tags of interests are also persisted and have a human readable descriptions. */ class DICOMTagsOfInterestService: public IDICOMTagsOfInterest { public: DICOMTagsOfInterestService(); ~DICOMTagsOfInterestService() override; void AddTagOfInterest(const DICOMTagPath& tag, bool makePersistant = true) override; DICOMTagPathMapType GetTagsOfInterest() const override; bool HasTag(const DICOMTagPath& tag) const override; void RemoveTag(const DICOMTagPath& tag) override; void RemoveAllTags() override; private: typedef std::set InternalTagSetType; - typedef itk::MutexLockHolder MutexHolder; + typedef std::lock_guard MutexHolder; InternalTagSetType m_Tags; - mutable itk::SimpleFastMutexLock m_Lock; + mutable std::mutex m_Lock; DICOMTagsOfInterestService(const DICOMTagsOfInterestService&); DICOMTagsOfInterestService& operator=(const DICOMTagsOfInterestService&); }; } #endif diff --git a/Modules/DICOM/include/mitkDICOMITKSeriesGDCMReader.h b/Modules/DICOM/include/mitkDICOMITKSeriesGDCMReader.h index 743bb72645..cf01900334 100644 --- a/Modules/DICOM/include/mitkDICOMITKSeriesGDCMReader.h +++ b/Modules/DICOM/include/mitkDICOMITKSeriesGDCMReader.h @@ -1,377 +1,377 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitkDICOMITKSeriesGDCMReader_h #define mitkDICOMITKSeriesGDCMReader_h +#include #include -#include "itkMutexLock.h" #include "mitkDICOMFileReader.h" #include "mitkDICOMDatasetSorter.h" #include "mitkDICOMGDCMImageFrameInfo.h" #include "mitkEquiDistantBlocksSorter.h" #include "mitkNormalDirectionConsistencySorter.h" #include "MitkDICOMExports.h" namespace itk { class TimeProbesCollectorBase; } namespace mitk { /** \ingroup DICOMModule \brief Flexible reader based on itk::ImageSeriesReader and GDCM, for single-slice modalities like CT, MR, PET, CR, etc. Implements the loading processed as structured by DICOMFileReader offers configuration of its loading strategy. Documentation sections: - \ref DICOMITKSeriesGDCMReader_LoadingStrategy - \ref DICOMITKSeriesGDCMReader_ForcedConfiguration - \ref DICOMITKSeriesGDCMReader_UserConfiguration - \ref DICOMITKSeriesGDCMReader_GantryTilt - \ref DICOMITKSeriesGDCMReader_Testing - \ref DICOMITKSeriesGDCMReader_Internals - \ref DICOMITKSeriesGDCMReader_RelatedClasses - \ref DICOMITKSeriesGDCMReader_TiltInternals - \ref DICOMITKSeriesGDCMReader_Condensing \section DICOMITKSeriesGDCMReader_LoadingStrategy Loading strategy The set of input files is processed by a number of DICOMDatasetSorter objects which may do two sort of things: 1. split a list of input frames into multiple lists, based on DICOM tags such as "Rows", "Columns", which cannot be mixed within a single mitk::Image 2. sort the frames within the input lists, based on the values of DICOM tags such as "Image Position Patient" When the DICOMITKSeriesGDCMReader is configured with DICOMDatasetSorter%s, the list of input files is processed as follows: 1. build an initial set of output groups, simply by grouping all input files. 2. for each configured DICOMDatasetSorter, process: - for each output group: 1. set this group's files as input to the sorter 2. let the sorter sort (and split) 3. integrate the sorter's output groups with our own output groups \section DICOMITKSeriesGDCMReader_ForcedConfiguration Forced Configuration In all cases, the reader will add two DICOMDatasetSorter objects that are required to load mitk::Images properly via itk::ImageSeriesReader: 1. As a \b first step, the input files will be split into groups that are not compatible because they differ in essential aspects: - (0028,0010) Number of Rows - (0028,0011) Number of Columns - (0028,0030) Pixel Spacing - (0018,1164) Imager Pixel Spacing - (0020,0037) %Image Orientation (Patient) - (0018,0050) Slice Thickness - (0028,0008) Number of Frames 2. As are two forced \b last steps: 1. There will always be an instance of EquiDistantBlocksSorter, which ensures that there is an equal distance between all the frames of an Image. This is required to achieve correct geometrical positions in the mitk::Image, i.e. it is essential to be able to make measurements in images. - whether or not the distance is required to be orthogonal to the image planes is configured by SetFixTiltByShearing(). - during this check, we need to tolerate some minor errors in documented vs. calculated image origins. The amount of tolerance can be adjusted by SetToleratedOriginOffset() and SetToleratedOriginOffsetToAdaptive(). Please see EquiDistantBlocksSorter for more details. The default should be good for most cases. 2. There is always an instance of NormalDirectionConsistencySorter, which makes the order of images go along the image normals (see NormalDirectionConsistencySorter) \section DICOMITKSeriesGDCMReader_UserConfiguration User Configuration The user of this class can add more sorting steps (similar to the one described in above section) by calling AddSortingElement(). Usually, an application will add sorting by "Image Position Patient", by "Instance Number", and by other relevant tags here. \section DICOMITKSeriesGDCMReader_GantryTilt Gantry tilt handling When CT gantry tilt is used, the gantry plane (= X-Ray source and detector ring) and the vertical plane do not align anymore. This scanner feature is used for example to reduce metal artifacs (e.g. Lee C , Evaluation of Using CT Gantry Tilt Scan on Head and Neck Cancer Patients with Dental Structure: Scans Show Less Metal Artifacts. Presented at: Radiological Society of North America 2011 Scientific Assembly and Annual Meeting; November 27- December 2, 2011 Chicago IL.). The acquired planes of such CT series do not match the expectations of a orthogonal geometry in mitk::Image: if you stack the slices, they show a small shift along the Y axis: \verbatim without tilt with tilt |||||| ////// |||||| ////// -- |||||| --------- ////// -------- table orientation |||||| ////// |||||| ////// Stacked slices: without tilt with tilt -------------- -------------- -------------- -------------- -------------- -------------- -------------- -------------- -------------- -------------- \endverbatim As such gemetries do not "work" in conjunction with mitk::Image, DICOMITKSeriesGDCMReader is able to perform a correction for such series. Whether or not such correction should be attempted is controlled by SetFixTiltByShearing(), the default being correction. For details, see "Internals" below. \section DICOMITKSeriesGDCMReader_Testing Testing A number of tests is implemented in module DICOMTesting, which is documented at \ref DICOMTesting. \section DICOMITKSeriesGDCMReader_Internals Class internals Internally, the class is based on GDCM and it depends heavily on the gdcm::Scanner class. Since the sorting elements (see DICOMDatasetSorter and DICOMSortCriterion) can access tags only via the DICOMDatasetAccess interface, BUT DICOMITKSeriesGDCMReader holds a list of more specific classes DICOMGDCMImageFrameInfo, we must convert between the two types sometimes. This explains the methods ToDICOMDatasetList(), FromDICOMDatasetList(). The intermediate result of all the sorting efforts is held in m_SortingResultInProgress, which is modified through InternalExecuteSortingStep(). \subsection DICOMITKSeriesGDCMReader_RelatedClasses Overview of related classes The following diagram gives an overview of the related classes: \image html implementeditkseriesgdcmreader.jpg \subsection DICOMITKSeriesGDCMReader_TiltInternals Details about the tilt correction The gantry tilt "correction" algorithm fixes two errors introduced by ITK's ImageSeriesReader: - the plane shift that is ignored by ITK's reader is recreated by applying a shearing transformation using itk::ResampleFilter. - the spacing is corrected (it is calculated by ITK's reader from the distance between two origins, which is NOT the slice distance in this special case) Both errors are introduced in itkImageSeriesReader.txx (ImageSeriesReader::GenerateOutputInformation(void)), lines 176 to 245 (as of ITK 3.20) For the correction, we examine two consecutive slices of a series, both described as a pair (origin/orientation): - we calculate if the first origin is on a line along the normal of the second slice - if this is not the case, the geometry will not fit a normal mitk::Image/mitk::%Geometry3D - we then project the second origin into the first slice's coordinate system to quantify the shift - both is done in class GantryTiltInformation with quite some comments. The geometry of image stacks with tilted geometries is illustrated below: - green: the DICOM images as described by their tags: origin as a point with the line indicating the orientation - red: the output of ITK ImageSeriesReader: wrong, larger spacing, no tilt - blue: how much a shear must correct \image html Modules/DICOM/doc/Doxygen/tilt-correction.jpg \subsection DICOMITKSeriesGDCMReader_Condensing Sub-classes can condense multiple blocks into a single larger block The sorting/splitting process described above is helpful for at least two more DICOM readers, which either try to load 3D+t images or which load diffusion data. In both cases, a single pixel of the mitk::Image is made up of multiple values, in one case values over time, in the other case multiple measurements of a single point. The specialized readers for these cases (e.g. ThreeDnTDICOMSeriesReader) can reuse most of the methods in DICOMITKSeriesGDCMReader, except that they need an extra step after the usual sorting, in which they can merge already grouped 3D blocks. What blocks are merged depends on the specialized reader's understanding of these images. To allow for such merging, a method Condense3DBlocks() is called as an absolute last step of AnalyzeInputFiles(). Given this, a sub-class could implement only LoadImages() and Condense3DBlocks() instead repeating most of AnalyzeInputFiles(). */ class MITKDICOM_EXPORT DICOMITKSeriesGDCMReader : public DICOMFileReader { public: mitkClassMacro( DICOMITKSeriesGDCMReader, DICOMFileReader ); mitkCloneMacro( DICOMITKSeriesGDCMReader ); itkFactorylessNewMacro( DICOMITKSeriesGDCMReader ); mitkNewMacro1Param( DICOMITKSeriesGDCMReader, unsigned int ); mitkNewMacro2Param( DICOMITKSeriesGDCMReader, unsigned int, bool ); /** \brief Runs the sorting / splitting process described in \ref DICOMITKSeriesGDCMReader_LoadingStrategy. Method required by DICOMFileReader. */ void AnalyzeInputFiles() override; // void AllocateOutputImages(); /** \brief Loads images using itk::ImageSeriesReader, potentially applies shearing to correct gantry tilt. */ bool LoadImages() override; // re-implemented from super-class bool CanHandleFile(const std::string& filename) override; /** \brief Add an element to the sorting procedure described in \ref DICOMITKSeriesGDCMReader_LoadingStrategy. */ virtual void AddSortingElement(DICOMDatasetSorter* sorter, bool atFront = false); typedef const std::list ConstSorterList; ConstSorterList GetFreelyConfiguredSortingElements() const; /** \brief Controls whether to "fix" tilted acquisitions by shearing the output (see \ref DICOMITKSeriesGDCMReader_GantryTilt). */ void SetFixTiltByShearing(bool on); bool GetFixTiltByShearing() const; /** \brief Controls whether groups of only two images are accepted when ensuring consecutive slices via EquiDistantBlocksSorter. */ void SetAcceptTwoSlicesGroups(bool accept) const; bool GetAcceptTwoSlicesGroups() const; /** \brief See \ref DICOMITKSeriesGDCMReader_ForcedConfiguration. */ void SetToleratedOriginOffsetToAdaptive(double fractionOfInterSliceDistanct = 0.3) const; /** \brief See \ref DICOMITKSeriesGDCMReader_ForcedConfiguration. */ void SetToleratedOriginOffset(double millimeters = 0.005) const; /** \brief Ignore all dicom tags that are non-essential for simple 3D volume import. */ void SetSimpleVolumeReading(bool read) { m_SimpleVolumeReading = read; }; /** \brief Ignore all dicom tags that are non-essential for simple 3D volume import. */ bool GetSimpleVolumeReading() { return m_SimpleVolumeReading; }; double GetToleratedOriginError() const; bool IsToleratedOriginOffsetAbsolute() const; double GetDecimalPlacesForOrientation() const; bool operator==(const DICOMFileReader& other) const override; DICOMTagPathList GetTagsOfInterest() const override; static int GetDefaultDecimalPlacesForOrientation() { return m_DefaultDecimalPlacesForOrientation; } static bool GetDefaultSimpleVolumeImport() { return m_DefaultSimpleVolumeImport; } static bool GetDefaultFixTiltByShearing() { return m_DefaultFixTiltByShearing; } protected: void InternalPrintConfiguration(std::ostream& os) const override; /// \brief Return active C locale static std::string GetActiveLocale(); /** \brief Remember current locale on stack, activate "C" locale. "C" locale is required for correct parsing of numbers by itk::ImageSeriesReader */ void PushLocale() const; /** \brief Activate last remembered locale from locale stack "C" locale is required for correct parsing of numbers by itk::ImageSeriesReader */ void PopLocale() const; const static int m_DefaultDecimalPlacesForOrientation = 5; const static bool m_DefaultSimpleVolumeImport = false; const static bool m_DefaultFixTiltByShearing = true; DICOMITKSeriesGDCMReader(unsigned int decimalPlacesForOrientation = m_DefaultDecimalPlacesForOrientation, bool simpleVolumeImport = m_DefaultSimpleVolumeImport); ~DICOMITKSeriesGDCMReader() override; DICOMITKSeriesGDCMReader(const DICOMITKSeriesGDCMReader& other); DICOMITKSeriesGDCMReader& operator=(const DICOMITKSeriesGDCMReader& other); typedef std::vector SortingBlockList; /** \brief "Hook" for sub-classes, see \ref DICOMITKSeriesGDCMReader_Condensing \return REMAINING blocks */ virtual SortingBlockList Condense3DBlocks(SortingBlockList& resultOf3DGrouping); virtual DICOMTagCache::Pointer GetTagCache() const; void SetTagCache( const DICOMTagCache::Pointer& ) override; /// \brief Sorting step as described in \ref DICOMITKSeriesGDCMReader_LoadingStrategy static SortingBlockList InternalExecuteSortingStep( unsigned int sortingStepIndex, const DICOMDatasetSorter::Pointer& sorter, const SortingBlockList& input); /// \brief Loads the mitk::Image by means of an itk::ImageSeriesReader virtual bool LoadMitkImageForOutput(unsigned int o); virtual bool LoadMitkImageForImageBlockDescriptor(DICOMImageBlockDescriptor& block) const; /// \brief Describe this reader's confidence for given SOP class UID static ReaderImplementationLevel GetReaderImplementationLevel(const std::string sopClassUID); private: /// \brief Creates the required sorting steps described in \ref DICOMITKSeriesGDCMReader_ForcedConfiguration void EnsureMandatorySortersArePresent(unsigned int decimalPlacesForOrientation, bool simpleVolumeImport = false); protected: // NOT nice, made available to ThreeDnTDICOMSeriesReader due to lack of time bool m_FixTiltByShearing; // could be removed by ITKDICOMSeriesReader NOT flagging tilt unless requested to fix it! bool m_SimpleVolumeReading; private: SortingBlockList m_SortingResultInProgress; typedef std::list SorterList; SorterList m_Sorter; protected: // NOT nice, made available to ThreeDnTDICOMSeriesReader and ClassicDICOMSeriesReader due to lack of time mitk::EquiDistantBlocksSorter::Pointer m_EquiDistantBlocksSorter; mitk::NormalDirectionConsistencySorter::Pointer m_NormalDirectionConsistencySorter; private: - static itk::MutexLock::Pointer s_LocaleMutex; + static std::mutex s_LocaleMutex; mutable std::stack m_ReplacedCLocales; mutable std::stack m_ReplacedCinLocales; double m_DecimalPlacesForOrientation; DICOMTagCache::Pointer m_TagCache; bool m_ExternalCache; }; } #endif diff --git a/Modules/DICOM/include/mitkDICOMTagScanner.h b/Modules/DICOM/include/mitkDICOMTagScanner.h index 374899f943..15abdbc3d0 100644 --- a/Modules/DICOM/include/mitkDICOMTagScanner.h +++ b/Modules/DICOM/include/mitkDICOMTagScanner.h @@ -1,118 +1,118 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitkDICOMTagScanner_h #define mitkDICOMTagScanner_h #include -#include "itkMutexLock.h" +#include #include "mitkDICOMEnums.h" #include "mitkDICOMTagPath.h" #include "mitkDICOMTagCache.h" #include "mitkDICOMDatasetAccessingImageFrameInfo.h" namespace mitk { /** \ingroup DICOMModule \brief Abstracts the tag scanning process for a set of DICOM files. Formerly integrated as a part of DICOMITKSeriesGDCMReader, the tag scanning part has been factored out into DICOMTagScanner classes in order to allow a single scan for multiple reader alternatives. This helps much in the selection process of e.g. DICOMFileReaderSelector. This is an abstract base class for concrete scanner implementations. @remark When used in a process where multiple classes will access the scan results, care should be taken that all the tags and files of interest are communicated to DICOMTagScanner before requesting the results! */ class MITKDICOM_EXPORT DICOMTagScanner : public itk::Object { public: mitkClassMacroItkParent(DICOMTagScanner, itk::Object); /** \brief Add this tag to the scanning process. */ virtual void AddTag(const DICOMTag& tag) = 0; /** \brief Add a list of tags to the scanning process. */ virtual void AddTags(const DICOMTagList& tags) = 0; /** \brief Add this tag path to the scanning process. */ virtual void AddTagPath(const DICOMTagPath& path) = 0; /** \brief Add a list of tag pathes to the scanning process. */ virtual void AddTagPaths(const DICOMTagPathList& paths) = 0; /** \brief Define the list of files to scan. This does not ADD to an internal list, but it replaces the whole list of files. */ virtual void SetInputFiles(const StringList& filenames) = 0; /** \brief Start the scanning process. Calling Scan() will invalidate previous scans, forgetting all about files and tags from files that have been scanned previously. */ virtual void Scan() = 0; /** \brief Retrieve a result list for file-by-file tag access. */ virtual DICOMDatasetAccessingImageFrameList GetFrameInfoList() const = 0; /** \brief Retrieve Pointer to the complete cache of the scan. */ virtual DICOMTagCache::Pointer GetScanCache() const = 0; protected: /** \brief Return active C locale */ static std::string GetActiveLocale(); /** \brief Remember current locale on stack, activate "C" locale. "C" locale is required for correct parsing of numbers by itk::ImageSeriesReader */ void PushLocale() const; /** \brief Activate last remembered locale from locale stack "C" locale is required for correct parsing of numbers by itk::ImageSeriesReader */ void PopLocale() const; DICOMTagScanner(); ~DICOMTagScanner() override; private: - static itk::MutexLock::Pointer s_LocaleMutex; + static std::mutex s_LocaleMutex; mutable std::stack m_ReplacedCLocales; mutable std::stack m_ReplacedCinLocales; DICOMTagScanner(const DICOMTagScanner&); }; } #endif diff --git a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel.cpp b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel.cpp index 11b1d88ecf..0499b2d5cb 100644 --- a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel.cpp +++ b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel.cpp @@ -1,58 +1,58 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDicomSeriesReader.txx" namespace mitk { Image::Pointer DicomSeriesReader::MultiplexLoadDICOMByITKRGBPixel(const StringContainer &filenames, bool correctTilt, const GantryTiltInformation &tiltInfo, DcmIoType::Pointer &io, CallbackCommand *command, Image::Pointer preLoadedImageBlock) { switch (io->GetComponentType()) { - case DcmIoType::UCHAR: + case itk::IOComponentEnum::UCHAR: return LoadDICOMByITK>( filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::CHAR: + case itk::IOComponentEnum::CHAR: return LoadDICOMByITK>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::USHORT: + case itk::IOComponentEnum::USHORT: return LoadDICOMByITK>( filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::SHORT: + case itk::IOComponentEnum::SHORT: return LoadDICOMByITK>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::UINT: + case itk::IOComponentEnum::UINT: return LoadDICOMByITK>( filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::INT: + case itk::IOComponentEnum::INT: return LoadDICOMByITK>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::ULONG: + case itk::IOComponentEnum::ULONG: return LoadDICOMByITK>( filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::LONG: + case itk::IOComponentEnum::LONG: return LoadDICOMByITK>( filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::FLOAT: + case itk::IOComponentEnum::FLOAT: return LoadDICOMByITK>(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::DOUBLE: + case itk::IOComponentEnum::DOUBLE: return LoadDICOMByITK>( filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); default: MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); return nullptr; } } } // end namespace mitk diff --git a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel4D.cpp b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel4D.cpp index 1c6d31bb1f..b994049e43 100644 --- a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel4D.cpp +++ b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMRGBPixel4D.cpp @@ -1,63 +1,63 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDicomSeriesReader.txx" namespace mitk { Image::Pointer DicomSeriesReader::MultiplexLoadDICOMByITK4DRGBPixel(std::list &imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation &tiltInfo, DcmIoType::Pointer &io, CallbackCommand *command, Image::Pointer preLoadedImageBlock) { switch (io->GetComponentType()) { - case DcmIoType::UCHAR: + case itk::IOComponentEnum::UCHAR: return LoadDICOMByITK4D>( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::CHAR: + case itk::IOComponentEnum::CHAR: return LoadDICOMByITK4D>( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::USHORT: + case itk::IOComponentEnum::USHORT: return LoadDICOMByITK4D>( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::SHORT: + case itk::IOComponentEnum::SHORT: return LoadDICOMByITK4D>( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::UINT: + case itk::IOComponentEnum::UINT: return LoadDICOMByITK4D>( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::INT: + case itk::IOComponentEnum::INT: return LoadDICOMByITK4D>( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::ULONG: + case itk::IOComponentEnum::ULONG: return LoadDICOMByITK4D>( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::LONG: + case itk::IOComponentEnum::LONG: return LoadDICOMByITK4D>( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::FLOAT: + case itk::IOComponentEnum::FLOAT: return LoadDICOMByITK4D>( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::DOUBLE: + case itk::IOComponentEnum::DOUBLE: return LoadDICOMByITK4D>( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); default: MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); return nullptr; } } } // end namespace mitk diff --git a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar.cpp b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar.cpp index 46c8dc61b4..dae0bb8510 100644 --- a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar.cpp +++ b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar.cpp @@ -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. ============================================================================*/ #include "mitkDicomSeriesReader.txx" namespace mitk { Image::Pointer DicomSeriesReader::MultiplexLoadDICOMByITKScalar(const StringContainer &filenames, bool correctTilt, const GantryTiltInformation &tiltInfo, DcmIoType::Pointer &io, CallbackCommand *command, Image::Pointer preLoadedImageBlock) { switch (io->GetComponentType()) { - case DcmIoType::UCHAR: + case itk::IOComponentEnum::UCHAR: return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::CHAR: + case itk::IOComponentEnum::CHAR: return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::USHORT: + case itk::IOComponentEnum::USHORT: return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::SHORT: + case itk::IOComponentEnum::SHORT: return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::UINT: + case itk::IOComponentEnum::UINT: return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::INT: + case itk::IOComponentEnum::INT: return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::ULONG: + case itk::IOComponentEnum::ULONG: return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::LONG: + case itk::IOComponentEnum::LONG: return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::FLOAT: + case itk::IOComponentEnum::FLOAT: return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::DOUBLE: + case itk::IOComponentEnum::DOUBLE: return LoadDICOMByITK(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); default: MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); return nullptr; } } } // end namespace mitk #include diff --git a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar4D.cpp b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar4D.cpp index e5239e4d6c..3def708d24 100644 --- a/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar4D.cpp +++ b/Modules/DICOM/src/legacy/mitkDicomSR_LoadDICOMScalar4D.cpp @@ -1,65 +1,65 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkDicomSeriesReader.txx" namespace mitk { Image::Pointer DicomSeriesReader::MultiplexLoadDICOMByITK4DScalar(std::list &imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation &tiltInfo, DcmIoType::Pointer &io, CallbackCommand *command, Image::Pointer preLoadedImageBlock) { switch (io->GetComponentType()) { - case DcmIoType::UCHAR: + case itk::IOComponentEnum::UCHAR: return LoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::CHAR: + case itk::IOComponentEnum::CHAR: return LoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::USHORT: + case itk::IOComponentEnum::USHORT: return LoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::SHORT: + case itk::IOComponentEnum::SHORT: return LoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::UINT: + case itk::IOComponentEnum::UINT: return LoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::INT: + case itk::IOComponentEnum::INT: return LoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::ULONG: + case itk::IOComponentEnum::ULONG: return LoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::LONG: + case itk::IOComponentEnum::LONG: return LoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::FLOAT: + case itk::IOComponentEnum::FLOAT: return LoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); - case DcmIoType::DOUBLE: + case itk::IOComponentEnum::DOUBLE: return LoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); default: MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); return nullptr; } } } // end namespace mitk #include diff --git a/Modules/DICOM/src/legacy/mitkDicomSeriesReader.cpp b/Modules/DICOM/src/legacy/mitkDicomSeriesReader.cpp index ac3cf8bbe5..df1ca813e5 100644 --- a/Modules/DICOM/src/legacy/mitkDicomSeriesReader.cpp +++ b/Modules/DICOM/src/legacy/mitkDicomSeriesReader.cpp @@ -1,1860 +1,1860 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // uncomment for learning more about the internal sorting mechanisms //#define MBILOG_ENABLE_DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkProperties.h" namespace mitk { std::string DicomSeriesReader::ReaderImplementationLevelToString(const ReaderImplementationLevel &enumValue) { switch (enumValue) { case ReaderImplementationLevel_Supported: return "Supported"; case ReaderImplementationLevel_PartlySupported: return "PartlySupported"; case ReaderImplementationLevel_Implemented: return "Implemented"; case ReaderImplementationLevel_Unsupported: return "Unsupported"; default: return ""; }; } std::string DicomSeriesReader::PixelSpacingInterpretationToString(const PixelSpacingInterpretation &enumValue) { switch (enumValue) { case PixelSpacingInterpretation_SpacingInPatient: return "In Patient"; case PixelSpacingInterpretation_SpacingAtDetector: return "At Detector"; case PixelSpacingInterpretation_SpacingUnknown: return "Unknown spacing"; default: return ""; }; } const DicomSeriesReader::TagToPropertyMapType &DicomSeriesReader::GetDICOMTagsToMITKPropertyMap() { static bool initialized = false; static TagToPropertyMapType dictionary; if (!initialized) { /* Selection criteria: - no sequences because we cannot represent that - nothing animal related (specied, breed registration number), MITK focusses on human medical image processing. - only general attributes so far When extending this, we should make use of a real dictionary (GDCM/DCMTK and lookup the tag names there) */ // Patient module dictionary["0010|0010"] = "dicom.patient.PatientsName"; dictionary["0010|0020"] = "dicom.patient.PatientID"; dictionary["0010|0030"] = "dicom.patient.PatientsBirthDate"; dictionary["0010|0040"] = "dicom.patient.PatientsSex"; dictionary["0010|0032"] = "dicom.patient.PatientsBirthTime"; dictionary["0010|1000"] = "dicom.patient.OtherPatientIDs"; dictionary["0010|1001"] = "dicom.patient.OtherPatientNames"; dictionary["0010|2160"] = "dicom.patient.EthnicGroup"; dictionary["0010|4000"] = "dicom.patient.PatientComments"; dictionary["0012|0062"] = "dicom.patient.PatientIdentityRemoved"; dictionary["0012|0063"] = "dicom.patient.DeIdentificationMethod"; // General Study module dictionary["0020|000d"] = "dicom.study.StudyInstanceUID"; dictionary["0008|0020"] = "dicom.study.StudyDate"; dictionary["0008|0030"] = "dicom.study.StudyTime"; dictionary["0008|0090"] = "dicom.study.ReferringPhysiciansName"; dictionary["0020|0010"] = "dicom.study.StudyID"; dictionary["0008|0050"] = "dicom.study.AccessionNumber"; dictionary["0008|1030"] = "dicom.study.StudyDescription"; dictionary["0008|1048"] = "dicom.study.PhysiciansOfRecord"; dictionary["0008|1060"] = "dicom.study.NameOfPhysicianReadingStudy"; // General Series module dictionary["0008|0060"] = "dicom.series.Modality"; dictionary["0020|000e"] = "dicom.series.SeriesInstanceUID"; dictionary["0020|0011"] = "dicom.series.SeriesNumber"; dictionary["0020|0060"] = "dicom.series.Laterality"; dictionary["0008|0021"] = "dicom.series.SeriesDate"; dictionary["0008|0031"] = "dicom.series.SeriesTime"; dictionary["0008|1050"] = "dicom.series.PerformingPhysiciansName"; dictionary["0018|1030"] = "dicom.series.ProtocolName"; dictionary["0008|103e"] = "dicom.series.SeriesDescription"; dictionary["0008|1070"] = "dicom.series.OperatorsName"; dictionary["0018|0015"] = "dicom.series.BodyPartExamined"; dictionary["0018|5100"] = "dicom.series.PatientPosition"; dictionary["0028|0108"] = "dicom.series.SmallestPixelValueInSeries"; dictionary["0028|0109"] = "dicom.series.LargestPixelValueInSeries"; // VOI LUT module dictionary["0028|1050"] = "dicom.voilut.WindowCenter"; dictionary["0028|1051"] = "dicom.voilut.WindowWidth"; dictionary["0028|1055"] = "dicom.voilut.WindowCenterAndWidthExplanation"; // Image Pixel module dictionary["0028|0004"] = "dicom.pixel.PhotometricInterpretation"; dictionary["0028|0010"] = "dicom.pixel.Rows"; dictionary["0028|0011"] = "dicom.pixel.Columns"; // Image Plane module dictionary["0028|0030"] = "dicom.PixelSpacing"; dictionary["0018|1164"] = "dicom.ImagerPixelSpacing"; // Misc dictionary["0008|0005"] = "dicom.SpecificCharacterSet"; initialized = true; } return dictionary; } DataNode::Pointer DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { DataNode::Pointer node = DataNode::New(); if (DicomSeriesReader::LoadDicomSeries( filenames, *node, sort, check_4d, correctTilt, callback, preLoadedImageBlock)) { if (filenames.empty()) { return nullptr; } return node; } else { return nullptr; } } bool DicomSeriesReader::LoadDicomSeries(const StringContainer &filenames, DataNode &node, bool sort, bool check_4d, bool correctTilt, UpdateCallBackMethod callback, itk::SmartPointer preLoadedImageBlock) { if (filenames.empty()) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; node.SetData(nullptr); return true; // this is not actually an error but the result is very simple } DcmIoType::Pointer io = DcmIoType::New(); try { if (io->CanReadFile(filenames.front().c_str())) { io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); - if (io->GetPixelType() == itk::ImageIOBase::SCALAR || io->GetPixelType() == itk::ImageIOBase::RGB) + if (io->GetPixelType() == itk::IOPixelEnum::SCALAR || io->GetPixelType() == itk::IOPixelEnum::RGB) { LoadDicom(filenames, node, sort, check_4d, correctTilt, callback, preLoadedImageBlock); } if (node.GetData()) { return true; } } } catch ( const itk::MemoryAllocationError &e ) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch ( const std::exception &e ) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch (...) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return false; } bool DicomSeriesReader::IsDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); return io->CanReadFile(filename.c_str()); } bool DicomSeriesReader::IsPhilips3DDicom(const std::string &filename) { DcmIoType::Pointer io = DcmIoType::New(); if (io->CanReadFile(filename.c_str())) { // Look at header Tag 3001,0010 if it is "Philips3D" gdcm::Reader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); if (data_set.FindDataElement(gdcm::Tag(0x3001, 0x0010)) && (sf.ToString(gdcm::Tag(0x3001, 0x0010)) == "Philips3D ")) { return true; } } return false; } bool DicomSeriesReader::ReadPhilips3DDicom(const std::string &filename, itk::SmartPointer output_image) { // Now get PhilipsSpecific Tags gdcm::PixmapReader reader; reader.SetFileName(filename.c_str()); reader.Read(); gdcm::DataSet &data_set = reader.GetFile().GetDataSet(); gdcm::StringFilter sf; sf.SetFile(reader.GetFile()); gdcm::Attribute<0x0028, 0x0011> dimTagX; // coloumns || sagittal gdcm::Attribute<0x3001, 0x1001, gdcm::VR::UL, gdcm::VM::VM1> dimTagZ; // I have no idea what is VM1. // (Philips specific) // axial gdcm::Attribute<0x0028, 0x0010> dimTagY; // rows || coronal gdcm::Attribute<0x0028, 0x0008> dimTagT; // how many frames gdcm::Attribute<0x0018, 0x602c> spaceTagX; // Spacing in X , unit is "physicalTagx" (usually centimeter) gdcm::Attribute<0x0018, 0x602e> spaceTagY; gdcm::Attribute<0x3001, 0x1003, gdcm::VR::FD, gdcm::VM::VM1> spaceTagZ; // (Philips specific) gdcm::Attribute<0x0018, 0x6024> physicalTagX; // if 3, then spacing params are centimeter gdcm::Attribute<0x0018, 0x6026> physicalTagY; gdcm::Attribute<0x3001, 0x1002, gdcm::VR::US, gdcm::VM::VM1> physicalTagZ; // (Philips specific) dimTagX.Set(data_set); dimTagY.Set(data_set); dimTagZ.Set(data_set); dimTagT.Set(data_set); spaceTagX.Set(data_set); spaceTagY.Set(data_set); spaceTagZ.Set(data_set); physicalTagX.Set(data_set); physicalTagY.Set(data_set); physicalTagZ.Set(data_set); unsigned int dimX = dimTagX.GetValue(), dimY = dimTagY.GetValue(), dimZ = dimTagZ.GetValue(), dimT = dimTagT.GetValue(), physicalX = physicalTagX.GetValue(), physicalY = physicalTagY.GetValue(), physicalZ = physicalTagZ.GetValue(); float spaceX = spaceTagX.GetValue(), spaceY = spaceTagY.GetValue(), spaceZ = spaceTagZ.GetValue(); if (physicalX == 3) // spacing parameter in cm, have to convert it to mm. spaceX = spaceX * 10; if (physicalY == 3) // spacing parameter in cm, have to convert it to mm. spaceY = spaceY * 10; if (physicalZ == 3) // spacing parameter in cm, have to convert it to mm. spaceZ = spaceZ * 10; // Ok, got all necessary Tags! // Now read Pixeldata (7fe0,0010) X x Y x Z x T Elements const gdcm::Pixmap &pixels = reader.GetPixmap(); gdcm::RAWCodec codec; codec.SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2); codec.SetPixelFormat(pixels.GetPixelFormat()); codec.SetPlanarConfiguration(0); gdcm::DataElement out; codec.Decode(data_set.GetDataElement(gdcm::Tag(0x7fe0, 0x0010)), out); const gdcm::ByteValue *bv = out.GetByteValue(); const char *new_pixels = bv->GetPointer(); // Create MITK Image + Geometry typedef itk::Image ImageType; // Pixeltype might be different sometimes? Maybe read it out from header ImageType::RegionType myRegion; ImageType::SizeType mySize; ImageType::IndexType myIndex; ImageType::SpacingType mySpacing; ImageType::Pointer imageItk = ImageType::New(); mySpacing[0] = spaceX; mySpacing[1] = spaceY; mySpacing[2] = spaceZ; mySpacing[3] = 1; myIndex[0] = 0; myIndex[1] = 0; myIndex[2] = 0; myIndex[3] = 0; mySize[0] = dimX; mySize[1] = dimY; mySize[2] = dimZ; mySize[3] = dimT; myRegion.SetSize(mySize); myRegion.SetIndex(myIndex); imageItk->SetSpacing(mySpacing); imageItk->SetRegions(myRegion); imageItk->Allocate(); imageItk->FillBuffer(0); itk::ImageRegionIterator iterator(imageItk, imageItk->GetLargestPossibleRegion()); iterator.GoToBegin(); unsigned long pixCount = 0; unsigned long planeSize = dimX * dimY; unsigned long planeCount = 0; unsigned long timeCount = 0; unsigned long numberOfSlices = dimZ; while (!iterator.IsAtEnd()) { unsigned long adressedPixel = pixCount + (numberOfSlices - 1 - planeCount) * planeSize // add offset to adress the first pixel of current plane + timeCount * numberOfSlices * planeSize; // add time offset iterator.Set(new_pixels[adressedPixel]); pixCount++; ++iterator; if (pixCount == planeSize) { pixCount = 0; planeCount++; } if (planeCount == numberOfSlices) { planeCount = 0; timeCount++; } if (timeCount == dimT) { break; } } mitk::CastToMitkImage(imageItk, output_image); return true; // actually never returns false yet.. but exception possible } std::string DicomSeriesReader::ConstCharStarToString(const char *s) { return s ? std::string(s) : std::string(); } bool DicomSeriesReader::DICOMStringToSpacing(const std::string &s, ScalarType &spacingX, ScalarType &spacingY) { bool successful = false; std::istringstream spacingReader(s); std::string spacing; if (std::getline(spacingReader, spacing, '\\')) { spacingY = atof(spacing.c_str()); if (std::getline(spacingReader, spacing, '\\')) { spacingX = atof(spacing.c_str()); successful = true; } } return successful; } Point3D DicomSeriesReader::DICOMStringToPoint3D(const std::string &s, bool &successful) { Point3D p; successful = true; std::istringstream originReader(s); std::string coordinate; unsigned int dim(0); while (std::getline(originReader, coordinate, '\\') && dim < 3) { p[dim++] = atof(coordinate.c_str()); } if (dim && dim != 3) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0032). Found " << dim << " instead of 3 values."; } else if (dim == 0) { successful = false; p.Fill(0.0); // assume default (0,0,0) } return p; } void DicomSeriesReader::DICOMStringToOrientationVectors(const std::string &s, Vector3D &right, Vector3D &up, bool &successful) { successful = true; std::istringstream orientationReader(s); std::string coordinate; unsigned int dim(0); while (std::getline(orientationReader, coordinate, '\\') && dim < 6) { if (dim < 3) { right[dim++] = atof(coordinate.c_str()); } else { up[dim++ - 3] = atof(coordinate.c_str()); } } if (dim && dim != 6) { successful = false; MITK_ERROR << "Reader implementation made wrong assumption on tag (0020,0037). Found " << dim << " instead of 6 values."; } else if (dim == 0) { // fill with defaults right.Fill(0.0); right[0] = 1.0; up.Fill(0.0); up[1] = 1.0; successful = false; } } DicomSeriesReader::SliceGroupingAnalysisResult DicomSeriesReader::AnalyzeFileForITKImageSeriesReaderSpacingAssumption( const StringContainer &files, bool groupImagesWithGantryTilt, const gdcm::Scanner::MappingType &tagValueMappings_) { // result.first = files that fit ITK's assumption // result.second = files that do not fit, should be run through // AnalyzeFileForITKImageSeriesReaderSpacingAssumption() // again SliceGroupingAnalysisResult result; // we const_cast here, because I could not use a map.at(), which would make the code much more readable auto &tagValueMappings = const_cast(tagValueMappings_); const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt Vector3D fromFirstToSecondOrigin; fromFirstToSecondOrigin.Fill(0.0); bool fromFirstToSecondOriginInitialized(false); Point3D thisOrigin; thisOrigin.Fill(0.0f); Point3D lastOrigin; lastOrigin.Fill(0.0f); Point3D lastDifferentOrigin; lastDifferentOrigin.Fill(0.0f); bool lastOriginInitialized(false); MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "Analyzing files for z-spacing assumption of ITK's ImageSeriesReader (group tilted: " << groupImagesWithGantryTilt << ")"; unsigned int fileIndex(0); for (auto fileIter = files.begin(); fileIter != files.end(); ++fileIter, ++fileIndex) { bool fileFitsIntoPattern(false); std::string thisOriginString; // Read tag value into point3D. PLEASE replace this by appropriate GDCM code if you figure out how to do that thisOriginString = ConstCharStarToString(tagValueMappings[fileIter->c_str()][tagImagePositionPatient]); if (thisOriginString.empty()) { // don't let such files be in a common group. Everything without position information will be loaded as a single // slice: // with standard DICOM files this can happen to: CR, DX, SC MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis (no position information)"; // we already have one occupying this position if (result.GetBlockFilenames().empty()) // nothing WITH position information yet { // ==> this is a group of its own, stop processing, come back later result.AddFileToSortedBlock(*fileIter); StringContainer remainingFiles; remainingFiles.insert(remainingFiles.end(), fileIter + 1, files.end()); result.AddFilesToUnsortedBlock(remainingFiles); fileFitsIntoPattern = false; break; // no files anymore } else { // ==> this does not match, consider later result.AddFileToUnsortedBlock(*fileIter); fileFitsIntoPattern = false; continue; // next file } } bool ignoredConversionError(-42); // hard to get here, no graceful way to react thisOrigin = DICOMStringToPoint3D(thisOriginString, ignoredConversionError); MITK_DEBUG << " " << fileIndex << " " << *fileIter << " at " /* << thisOriginString */ << "(" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; if (lastOriginInitialized && (thisOrigin == lastOrigin)) { MITK_DEBUG << " ==> Sort away " << *fileIter << " for separate time step"; // we already have one occupying this position result.AddFileToUnsortedBlock(*fileIter); fileFitsIntoPattern = false; } else { if (!fromFirstToSecondOriginInitialized && lastOriginInitialized) // calculate vector as soon as possible when we get a new position { fromFirstToSecondOrigin = thisOrigin - lastDifferentOrigin; fromFirstToSecondOriginInitialized = true; // Here we calculate if this slice and the previous one are well aligned, // i.e. we test if the previous origin is on a line through the current // origin, directed into the normal direction of the current slice. // If this is NOT the case, then we have a data set with a TILTED GANTRY geometry, // which cannot be simply loaded into a single mitk::Image at the moment. // For this case, we flag this finding in the result and DicomSeriesReader // can correct for that later. Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors( tagValueMappings[fileIter->c_str()][tagImageOrientation], right, up, ignoredConversionError); GantryTiltInformation tiltInfo(lastDifferentOrigin, thisOrigin, right, up, 1); if (tiltInfo.IsSheared()) // mitk::eps is too small; 1/1000 of a mm should be enough to detect tilt { /* optimistic approach, accepting gantry tilt: save file for later, check all further files */ // at this point we have TWO slices analyzed! if they are the only two files, we still split, because there // is // no third to verify our tilting assumption. // later with a third being available, we must check if the initial tilting vector is still valid. if yes, // continue. // if NO, we need to split the already sorted part (result.first) and the currently analyzed file // (*fileIter) // tell apart gantry tilt from overall skewedness // sort out irregularly sheared slices, that IS NOT tilting if (groupImagesWithGantryTilt && tiltInfo.IsRegularGantryTilt()) { // check if this is at least roughly the same angle as recorded in DICOM tags if (tagValueMappings[fileIter->c_str()].find(tagGantryTilt) != tagValueMappings[fileIter->c_str()].end()) { // read value, compare to calculated angle std::string tiltStr = ConstCharStarToString(tagValueMappings[fileIter->c_str()][tagGantryTilt]); double angle = atof(tiltStr.c_str()); MITK_DEBUG << "Comparing recorded tilt angle " << angle << " against calculated value " << tiltInfo.GetTiltAngleInDegrees(); // TODO we probably want the signs correct, too (that depends: this is just a rough check, nothing // serious) // TODO TODO TODO when angle -27 and tiltangle 63, this will never trigger the if-clause... useless // check // in this case! old bug..?! if (fabs(angle) - tiltInfo.GetTiltAngleInDegrees() > 0.25) { result.AddFileToUnsortedBlock(*fileIter); // sort away for further analysis fileFitsIntoPattern = false; } else // tilt angle from header is less than 0.25 degrees different from what we calculated, assume this // is // fine { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // we cannot check the calculated tilt angle against the one from the dicom header (so we assume we // are // right) { result.FlagGantryTilt(); result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // caller does not want tilt compensation OR shearing is more complicated than tilt { result.AddFileToUnsortedBlock(*fileIter); // sort away for further analysis fileFitsIntoPattern = false; } } else // not sheared { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else if (fromFirstToSecondOriginInitialized) // we already know the offset between slices { Point3D assumedOrigin = lastDifferentOrigin + fromFirstToSecondOrigin; Vector3D originError = assumedOrigin - thisOrigin; double norm = originError.GetNorm(); double toleratedError(0.005); // max. 1/10mm error when measurement crosses 20 slices in z direction if (norm > toleratedError) { MITK_DEBUG << " File does not fit into the inter-slice distance pattern (diff = " << norm << ", allowed " << toleratedError << ")."; MITK_DEBUG << " Expected position (" << assumedOrigin[0] << "," << assumedOrigin[1] << "," << assumedOrigin[2] << "), got position (" << thisOrigin[0] << "," << thisOrigin[1] << "," << thisOrigin[2] << ")"; MITK_DEBUG << " ==> Sort away " << *fileIter << " for later analysis"; // At this point we know we deviated from the expectation of ITK's ImageSeriesReader // We split the input file list at this point, i.e. all files up to this one (excluding it) // are returned as group 1, the remaining files (including the faulty one) are group 2 /* Optimistic approach: check if any of the remaining slices fits in */ result.AddFileToUnsortedBlock(*fileIter); // sort away for further analysis fileFitsIntoPattern = false; } else { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } else // this should be the very first slice { result.AddFileToSortedBlock(*fileIter); // this file is good for current block fileFitsIntoPattern = true; } } // record current origin for reference in later iterations if (!lastOriginInitialized || (fileFitsIntoPattern && (thisOrigin != lastOrigin))) { lastDifferentOrigin = thisOrigin; } lastOrigin = thisOrigin; lastOriginInitialized = true; } if (result.ContainsGantryTilt()) { // check here how many files were grouped. // IF it was only two files AND we assume tiltedness (e.g. save "distance") // THEN we would want to also split the two previous files (simple) because // we don't have any reason to assume they belong together if (result.GetBlockFilenames().size() == 2) { result.UndoPrematureGrouping(); } } return result; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer &files, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { return GetSeries(files, true, groupImagesWithGantryTilt, restrictions); } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const StringContainer &files, bool sortTo3DPlust, bool groupImagesWithGantryTilt, const StringContainer & /*restrictions*/) { /** assumption about this method: returns a map of uid-like-key --> list(filename) each entry should contain filenames that have images of same - series instance uid (automatically done by GDCMSeriesFileNames - 0020,0037 image orientation (patient) - 0028,0030 pixel spacing (x,y) - 0018,0050 slice thickness */ // use GDCM directly, itk::GDCMSeriesFileNames does not work with GDCM 2 // PART I: scan files for sorting relevant DICOM tags, // separate images that differ in any of those // attributes (they cannot possibly form a 3D block) // scan for relevant tags in dicom files gdcm::Scanner scanner; const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID scanner.AddTag(tagSOPClassUID); const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID scanner.AddTag(tagSeriesInstanceUID); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation scanner.AddTag(tagImageOrientation); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing scanner.AddTag(tagPixelSpacing); const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing scanner.AddTag(tagImagerPixelSpacing); const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness scanner.AddTag(tagSliceThickness); const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows scanner.AddTag(tagNumberOfRows); const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols scanner.AddTag(tagNumberOfColumns); const gdcm::Tag tagGantryTilt(0x0018, 0x1120); // gantry tilt scanner.AddTag(tagGantryTilt); const gdcm::Tag tagModality(0x0008, 0x0060); // modality scanner.AddTag(tagModality); const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames scanner.AddTag(tagNumberOfFrames); // additional tags read in this scan to allow later analysis // THESE tag are not used for initial separating of files const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image Position (Patient) scanner.AddTag(tagImagePositionPatient); // TODO add further restrictions from arguments (when anybody asks for it) FileNamesGrouping result; // let GDCM scan files if (!scanner.Scan(files)) { MITK_ERROR << "gdcm::Scanner failed when scanning " << files.size() << " input files."; return result; } // assign files IDs that will separate them for loading into image blocks for (auto fileIter = scanner.Begin(); fileIter != scanner.End(); ++fileIter) { if (std::string(fileIter->first).empty()) continue; // TODO understand why Scanner has empty string entries if (std::string(fileIter->first) == std::string("DICOMDIR")) continue; /* sort out multi-frame if ( scanner.GetValue( fileIter->first , tagNumberOfFrames ) ) { MITK_INFO << "Ignoring " << fileIter->first << " because we cannot handle multi-frame images."; continue; } */ // we const_cast here, because I could not use a map.at() function in CreateMoreUniqueSeriesIdentifier. // doing the same thing with find would make the code less readable. Since we forget the Scanner results // anyway after this function, we can simply tolerate empty map entries introduced by bad operator[] access std::string moreUniqueSeriesId = CreateMoreUniqueSeriesIdentifier(const_cast(fileIter->second)); result[moreUniqueSeriesId].AddFile(fileIter->first); } // PART II: sort slices spatially (or at least consistently if this is NOT possible, see method) for (FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter) { try { result[groupIter->first] = ImageBlockDescriptor(SortSeriesSlices(groupIter->second.GetFilenames())); // sort each slice group spatially } catch (...) { MITK_ERROR << "Caught something."; } } // PART III: analyze pre-sorted images for valid blocks (i.e. blocks of equal z-spacing), // separate into multiple blocks if necessary. // // Analysis performs the following steps: // * imitate itk::ImageSeriesReader: use the distance between the first two images as z-spacing // * check what images actually fulfill ITK's z-spacing assumption // * separate all images that fail the test into new blocks, re-iterate analysis for these blocks // * this includes images which DO NOT PROVIDE spatial information, i.e. all images w/o // ImagePositionPatient will be loaded separately FileNamesGrouping groupsOf3DPlusTBlocks; // final result of this function for (FileNamesGrouping::const_iterator groupIter = result.begin(); groupIter != result.end(); ++groupIter) { FileNamesGrouping groupsOf3DBlocks; // intermediate result for only this group(!) StringContainer filesStillToAnalyze = groupIter->second.GetFilenames(); std::string groupUID = groupIter->first; unsigned int subgroup(0); MITK_DEBUG << "Analyze group " << groupUID << " of " << groupIter->second.GetFilenames().size() << " files"; while (!filesStillToAnalyze.empty()) // repeat until all files are grouped somehow { SliceGroupingAnalysisResult analysisResult = AnalyzeFileForITKImageSeriesReaderSpacingAssumption( filesStillToAnalyze, groupImagesWithGantryTilt, scanner.GetMappings()); // enhance the UID for additional groups std::stringstream newGroupUID; newGroupUID << groupUID << '.' << subgroup; ImageBlockDescriptor thisBlock(analysisResult.GetBlockFilenames()); std::string firstFileInBlock = thisBlock.GetFilenames().front(); thisBlock.SetImageBlockUID(newGroupUID.str()); thisBlock.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagSeriesInstanceUID))); thisBlock.SetHasGantryTiltCorrected(analysisResult.ContainsGantryTilt()); thisBlock.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagSOPClassUID))); thisBlock.SetNumberOfFrames( ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagNumberOfFrames))); thisBlock.SetModality( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagModality))); thisBlock.SetPixelSpacingInformation( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagPixelSpacing)), DicomSeriesReader::ConstCharStarToString(scanner.GetValue(firstFileInBlock.c_str(), tagImagerPixelSpacing))); thisBlock.SetHasMultipleTimePoints(false); groupsOf3DBlocks[newGroupUID.str()] = thisBlock; // MITK_DEBUG << "Result: sorted 3D group " << newGroupUID.str() << " with " << groupsOf3DBlocks[ // newGroupUID.str() ].GetFilenames().size() << " files"; MITK_DEBUG << "Result: sorted 3D group with " << groupsOf3DBlocks[newGroupUID.str()].GetFilenames().size() << " files"; StringContainer debugOutputFiles = analysisResult.GetBlockFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " IN " << *siter; ++subgroup; filesStillToAnalyze = analysisResult.GetUnsortedFilenames(); // remember what needs further analysis for (StringContainer::const_iterator siter = filesStillToAnalyze.begin(); siter != filesStillToAnalyze.end(); ++siter) MITK_DEBUG << " OUT " << *siter; } // end of grouping, now post-process groups // PART IV: attempt to group blocks to 3D+t blocks if requested // inspect entries of groupsOf3DBlocks // - if number of files is identical to previous entry, collect for 3D+t block // - as soon as number of files changes from previous entry, record collected blocks as 3D+t block, // start // a new one, continue // decide whether or not to group 3D blocks into 3D+t blocks where possible if (!sortTo3DPlust) { // copy 3D blocks to output groupsOf3DPlusTBlocks.insert(groupsOf3DBlocks.begin(), groupsOf3DBlocks.end()); } else { // sort 3D+t (as described in "PART IV") MITK_DEBUG << "================================================================================"; MITK_DEBUG << "3D+t analysis:"; unsigned int numberOfFilesInPreviousBlock(0); std::string previousBlockKey; for (FileNamesGrouping::const_iterator block3DIter = groupsOf3DBlocks.begin(); block3DIter != groupsOf3DBlocks.end(); ++block3DIter) { unsigned int numberOfFilesInThisBlock = block3DIter->second.GetFilenames().size(); std::string thisBlockKey = block3DIter->first; if (numberOfFilesInPreviousBlock == 0) { numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; MITK_DEBUG << " 3D+t group " << thisBlockKey; previousBlockKey = thisBlockKey; } else { bool identicalOrigins; try { // check whether this and the previous block share a comon origin // TODO should be safe, but a little try/catch or other error handling wouldn't hurt const char *origin_value = scanner.GetValue(groupsOf3DBlocks[thisBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient), *previous_origin_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().front().c_str(), tagImagePositionPatient), *destination_value = scanner.GetValue( groupsOf3DBlocks[thisBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient), *previous_destination_value = scanner.GetValue( groupsOf3DBlocks[previousBlockKey].GetFilenames().back().c_str(), tagImagePositionPatient); if (!origin_value || !previous_origin_value || !destination_value || !previous_destination_value) { identicalOrigins = false; } else { std::string thisOriginString = ConstCharStarToString(origin_value); std::string previousOriginString = ConstCharStarToString(previous_origin_value); // also compare last origin, because this might differ if z-spacing is different std::string thisDestinationString = ConstCharStarToString(destination_value); std::string previousDestinationString = ConstCharStarToString(previous_destination_value); identicalOrigins = ((thisOriginString == previousOriginString) && (thisDestinationString == previousDestinationString)); } } catch (...) { identicalOrigins = false; } if (identicalOrigins && (numberOfFilesInPreviousBlock == numberOfFilesInThisBlock)) { // group with previous block groupsOf3DPlusTBlocks[previousBlockKey].AddFiles(block3DIter->second.GetFilenames()); groupsOf3DPlusTBlocks[previousBlockKey].SetHasMultipleTimePoints(true); MITK_DEBUG << " --> group enhanced with another timestep"; } else { // start a new block groupsOf3DPlusTBlocks[thisBlockKey] = block3DIter->second; int numberOfTimeSteps = groupsOf3DPlusTBlocks[previousBlockKey].GetFilenames().size() / numberOfFilesInPreviousBlock; MITK_DEBUG << " ==> group closed with " << numberOfTimeSteps << " time steps"; previousBlockKey = thisBlockKey; MITK_DEBUG << " 3D+t group " << thisBlockKey << " started"; } } numberOfFilesInPreviousBlock = numberOfFilesInThisBlock; } } } MITK_DEBUG << "================================================================================"; MITK_DEBUG << "Summary: "; for (FileNamesGrouping::const_iterator groupIter = groupsOf3DPlusTBlocks.begin(); groupIter != groupsOf3DPlusTBlocks.end(); ++groupIter) { ImageBlockDescriptor block = groupIter->second; MITK_DEBUG << " " << block.GetFilenames().size() << " '" << block.GetModality() << "' images (" << block.GetSOPClassUIDAsString() << ") in volume " << block.GetImageBlockUID(); MITK_DEBUG << " (gantry tilt : " << (block.HasGantryTiltCorrected() ? "Yes" : "No") << "; " "pixel spacing : " << PixelSpacingInterpretationToString(block.GetPixelSpacingType()) << "; " "3D+t: " << (block.HasMultipleTimePoints() ? "Yes" : "No") << "; " "reader support: " << ReaderImplementationLevelToString(block.GetReaderImplementationLevel()) << ")"; StringContainer debugOutputFiles = block.GetFilenames(); for (StringContainer::const_iterator siter = debugOutputFiles.begin(); siter != debugOutputFiles.end(); ++siter) MITK_DEBUG << " F " << *siter; } MITK_DEBUG << "================================================================================"; return groupsOf3DPlusTBlocks; } DicomSeriesReader::FileNamesGrouping DicomSeriesReader::GetSeries(const std::string &dir, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { gdcm::Directory directoryLister; directoryLister.Load(dir.c_str(), false); // non-recursive return GetSeries(directoryLister.GetFilenames(), groupImagesWithGantryTilt, restrictions); } std::string DicomSeriesReader::CreateSeriesIdentifierPart(gdcm::Scanner::TagToValue &tagValueMap, const gdcm::Tag &tag) { std::string result; try { result = IDifyTagValue(tagValueMap[tag] ? tagValueMap[tag] : std::string("")); } catch ( const std::exception & ) { // we are happy with even nothing, this will just group images of a series // MITK_WARN << "Could not access tag " << tag << ": " << e.what(); } return result; } std::string DicomSeriesReader::CreateMoreUniqueSeriesIdentifier(gdcm::Scanner::TagToValue &tagValueMap) { const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // image orientation const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing const gdcm::Tag tagSliceThickness(0x0018, 0x0050); // slice thickness const gdcm::Tag tagNumberOfRows(0x0028, 0x0010); // number rows const gdcm::Tag tagNumberOfColumns(0x0028, 0x0011); // number cols const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames const char *tagSeriesInstanceUid = tagValueMap[tagSeriesInstanceUID]; if (!tagSeriesInstanceUid) { mitkThrow() << "CreateMoreUniqueSeriesIdentifier() could not access series instance UID. Something is seriously " "wrong with this image, so stopping here."; } std::string constructedID = tagSeriesInstanceUid; constructedID += CreateSeriesIdentifierPart(tagValueMap, tagNumberOfRows); constructedID += CreateSeriesIdentifierPart(tagValueMap, tagNumberOfColumns); constructedID += CreateSeriesIdentifierPart(tagValueMap, tagPixelSpacing); constructedID += CreateSeriesIdentifierPart(tagValueMap, tagImagerPixelSpacing); constructedID += CreateSeriesIdentifierPart(tagValueMap, tagSliceThickness); constructedID += CreateSeriesIdentifierPart(tagValueMap, tagNumberOfFrames); // be a bit tolerant for orienatation, let only the first few digits matter // (http://bugs.mitk.org/show_bug.cgi?id=12263) // NOT constructedID += CreateSeriesIdentifierPart( tagValueMap, tagImageOrientation ); if (tagValueMap.find(tagImageOrientation) != tagValueMap.end()) { bool conversionError(false); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); DICOMStringToOrientationVectors(tagValueMap[tagImageOrientation], right, up, conversionError); // string newstring sprintf(simplifiedOrientationString, "%.3f\\%.3f\\%.3f\\%.3f\\%.3f\\%.3f", right[0], // right[1], // right[2], up[0], up[1], up[2]); std::ostringstream ss; ss.setf(std::ios::fixed, std::ios::floatfield); ss.precision(5); ss << right[0] << "\\" << right[1] << "\\" << right[2] << "\\" << up[0] << "\\" << up[1] << "\\" << up[2]; std::string simplifiedOrientationString(ss.str()); constructedID += IDifyTagValue(simplifiedOrientationString); } constructedID.resize(constructedID.length() - 1); // cut of trailing '.' return constructedID; } std::string DicomSeriesReader::IDifyTagValue(const std::string &value) { std::string IDifiedValue(value); if (value.empty()) throw std::logic_error("IDifyTagValue() illegaly called with empty tag value"); // Eliminate non-alnum characters, including whitespace... // that may have been introduced by concats. for (std::size_t i = 0; i < IDifiedValue.size(); i++) { while (i < IDifiedValue.size() && !(IDifiedValue[i] == '.' || (IDifiedValue[i] >= 'a' && IDifiedValue[i] <= 'z') || (IDifiedValue[i] >= '0' && IDifiedValue[i] <= '9') || (IDifiedValue[i] >= 'A' && IDifiedValue[i] <= 'Z'))) { IDifiedValue.erase(i, 1); } } IDifiedValue += "."; return IDifiedValue; } DicomSeriesReader::StringContainer DicomSeriesReader::GetSeries(const std::string &dir, const std::string &series_uid, bool groupImagesWithGantryTilt, const StringContainer &restrictions) { FileNamesGrouping allSeries = GetSeries(dir, groupImagesWithGantryTilt, restrictions); StringContainer resultingFileList; for (FileNamesGrouping::const_iterator idIter = allSeries.begin(); idIter != allSeries.end(); ++idIter) { if (idIter->first.find(series_uid) == 0) // this ID starts with given series_uid { return idIter->second.GetFilenames(); } } return resultingFileList; } DicomSeriesReader::StringContainer DicomSeriesReader::SortSeriesSlices(const StringContainer &unsortedFilenames) { /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns (each piece of information except the rows/columns might be missing) sorting with GdcmSortFunction tries its best by sorting by spatial position and more hints (acquisition number, acquisition time, trigger time) but will always produce a sorting by falling back to SOP Instance UID. */ gdcm::Sorter sorter; sorter.SetSortFunction(DicomSeriesReader::GdcmSortFunction); try { if (sorter.Sort(unsortedFilenames)) { return sorter.GetFilenames(); } else { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } catch ( const std::logic_error & ) { MITK_WARN << "Sorting error. Leaving series unsorted."; return unsortedFilenames; } } bool DicomSeriesReader::GdcmSortFunction(const gdcm::DataSet &ds1, const gdcm::DataSet &ds2) { // This method MUST accept missing location and position information (and all else, too) // because we cannot rely on anything // (restriction on the sentence before: we have to provide consistent sorting, so we // rely on the minimum information all DICOM files need to provide: SOP Instance UID) /* we CAN expect a group of equal - series instance uid - image orientation - pixel spacing - imager pixel spacing - slice thickness - number of rows/columns */ static const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image Position (Patient) static const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation // see if we have Image Position and Orientation if (ds1.FindDataElement(tagImagePositionPatient) && ds1.FindDataElement(tagImageOrientation) && ds2.FindDataElement(tagImagePositionPatient) && ds2.FindDataElement(tagImageOrientation)) { gdcm::Attribute<0x0020, 0x0032> image_pos1; // Image Position (Patient) gdcm::Attribute<0x0020, 0x0037> image_orientation1; // Image Orientation (Patient) image_pos1.Set(ds1); image_orientation1.Set(ds1); gdcm::Attribute<0x0020, 0x0032> image_pos2; gdcm::Attribute<0x0020, 0x0037> image_orientation2; image_pos2.Set(ds2); image_orientation2.Set(ds2); /* we tolerate very small differences in image orientation, since we got to know about acquisitions where these values change across a single series (7th decimal digit) (http://bugs.mitk.org/show_bug.cgi?id=12263) still, we want to check if our assumption of 'almost equal' orientations is valid */ for (unsigned int dim = 0; dim < 6; ++dim) { if (fabs(image_orientation2[dim] - image_orientation1[dim]) > 0.0001) { MITK_ERROR << "Dicom images have different orientations."; throw std::logic_error( "Dicom images have different orientations. Call GetSeries() first to separate images."); } } double normal[3]; normal[0] = image_orientation1[1] * image_orientation1[5] - image_orientation1[2] * image_orientation1[4]; normal[1] = image_orientation1[2] * image_orientation1[3] - image_orientation1[0] * image_orientation1[5]; normal[2] = image_orientation1[0] * image_orientation1[4] - image_orientation1[1] * image_orientation1[3]; double dist1 = 0.0, dist2 = 0.0; // this computes the distance from world origin (0,0,0) ALONG THE NORMAL of the image planes for (unsigned char i = 0u; i < 3u; ++i) { dist1 += normal[i] * image_pos1[i]; dist2 += normal[i] * image_pos2[i]; } // if we can sort by just comparing the distance, we do exactly that if (fabs(dist1 - dist2) >= mitk::eps) { // default: compare position return dist1 < dist2; } else // we need to check more properties to distinguish slices { // try to sort by Acquisition Number static const gdcm::Tag tagAcquisitionNumber(0x0020, 0x0012); if (ds1.FindDataElement(tagAcquisitionNumber) && ds2.FindDataElement(tagAcquisitionNumber)) { gdcm::Attribute<0x0020, 0x0012> acquisition_number1; // Acquisition number gdcm::Attribute<0x0020, 0x0012> acquisition_number2; acquisition_number1.Set(ds1); acquisition_number2.Set(ds2); if (acquisition_number1 != acquisition_number2) { return acquisition_number1 < acquisition_number2; } else // neither position nor acquisition number are good for sorting, so check more { // try to sort by Acquisition Time static const gdcm::Tag tagAcquisitionTime(0x0008, 0x0032); if (ds1.FindDataElement(tagAcquisitionTime) && ds2.FindDataElement(tagAcquisitionTime)) { gdcm::Attribute<0x0008, 0x0032> acquisition_time1; // Acquisition time gdcm::Attribute<0x0008, 0x0032> acquisition_time2; acquisition_time1.Set(ds1); acquisition_time2.Set(ds2); if (acquisition_time1 != acquisition_time2) { return acquisition_time1 < acquisition_time2; } else // we gave up on image position, acquisition number and acquisition time now { // let's try trigger time static const gdcm::Tag tagTriggerTime(0x0018, 0x1060); if (ds1.FindDataElement(tagTriggerTime) && ds2.FindDataElement(tagTriggerTime)) { gdcm::Attribute<0x0018, 0x1060> trigger_time1; // Trigger time gdcm::Attribute<0x0018, 0x1060> trigger_time2; trigger_time1.Set(ds1); trigger_time2.Set(ds2); if (trigger_time1 != trigger_time2) { return trigger_time1 < trigger_time2; } // ELSE! // for this and many previous ifs we fall through if nothing lets us sort } // . } // . } // . } } } } // . // LAST RESORT: all valuable information for sorting is missing. // Sort by some meaningless but unique identifiers to satisfy the sort function static const gdcm::Tag tagSOPInstanceUID(0x0008, 0x0018); if (ds1.FindDataElement(tagSOPInstanceUID) && ds2.FindDataElement(tagSOPInstanceUID)) { MITK_DEBUG << "Dicom images are missing attributes for a meaningful sorting, falling back to SOP instance UID comparison."; gdcm::Attribute<0x0008, 0x0018> SOPInstanceUID1; // SOP instance UID is mandatory and unique gdcm::Attribute<0x0008, 0x0018> SOPInstanceUID2; SOPInstanceUID1.Set(ds1); SOPInstanceUID2.Set(ds2); return SOPInstanceUID1 < SOPInstanceUID2; } else { // no DICOM file should really come down here, this should only be reached with unskillful and unlucky // manipulation // of files std::string error_message("Malformed DICOM images, which do not even contain a SOP Instance UID."); MITK_ERROR << error_message; throw std::logic_error(error_message); } } std::string DicomSeriesReader::GetConfigurationString() { std::stringstream configuration; configuration << "MITK_USE_GDCMIO: "; configuration << "true"; configuration << "\n"; configuration << "GDCM_VERSION: "; #ifdef GDCM_MAJOR_VERSION configuration << GDCM_VERSION; #endif // configuration << "\n"; return configuration.str(); } void DicomSeriesReader::CopyMetaDataToImageProperties(StringContainer filenames, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, const ImageBlockDescriptor &blockInfo, Image *image) { std::list imageBlock; imageBlock.push_back(filenames); CopyMetaDataToImageProperties(imageBlock, tagValueMappings_, io, blockInfo, image); } void DicomSeriesReader::CopyMetaDataToImageProperties(std::list imageBlock, const gdcm::Scanner::MappingType &tagValueMappings_, DcmIoType *io, const ImageBlockDescriptor &blockInfo, Image *image) { if (!io || !image) return; StringLookupTable filesForSlices; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceNumberForSlices; auto &tagValueMappings = const_cast(tagValueMappings_); // DICOM tags which should be added to the image properties const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number unsigned int timeStep(0); std::string propertyKeySliceLocation = "dicom.image.0020.1041"; std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; // tags for each image for (auto i = imageBlock.begin(); i != imageBlock.end(); i++, timeStep++) { const StringContainer &files = (*i); unsigned int slice(0); for (auto fIter = files.begin(); fIter != files.end(); ++fIter, ++slice) { filesForSlices.SetTableValue(slice, *fIter); gdcm::Scanner::TagToValue tagValueMapForFile = tagValueMappings[fIter->c_str()]; if (tagValueMapForFile.find(tagSliceLocation) != tagValueMapForFile.end()) sliceLocationForSlices.SetTableValue(slice, tagValueMapForFile[tagSliceLocation]); if (tagValueMapForFile.find(tagInstanceNumber) != tagValueMapForFile.end()) instanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagInstanceNumber]); if (tagValueMapForFile.find(tagSOPInstanceNumber) != tagValueMapForFile.end()) SOPInstanceNumberForSlices.SetTableValue(slice, tagValueMapForFile[tagSOPInstanceNumber]); } image->SetProperty("files", StringLookupTableProperty::New(filesForSlices)); // If more than one time step add postfix ".t" + timestep if (timeStep != 0) { std::ostringstream postfix; postfix << ".t" << timeStep; propertyKeySliceLocation.append(postfix.str()); propertyKeyInstanceNumber.append(postfix.str()); propertyKeySOPInstanceNumber.append(postfix.str()); } image->SetProperty(propertyKeySliceLocation.c_str(), StringLookupTableProperty::New(sliceLocationForSlices)); image->SetProperty(propertyKeyInstanceNumber.c_str(), StringLookupTableProperty::New(instanceNumberForSlices)); image->SetProperty(propertyKeySOPInstanceNumber.c_str(), StringLookupTableProperty::New(SOPInstanceNumberForSlices)); } // Copy tags for series, study, patient level (leave interpretation to application). // These properties will be copied to the DataNode by DicomSeriesReader. // tags for the series (we just use the one that ITK copied to its dictionary (proably that of the last slice) const itk::MetaDataDictionary &dict = io->GetMetaDataDictionary(); const TagToPropertyMapType &propertyLookup = DicomSeriesReader::GetDICOMTagsToMITKPropertyMap(); auto dictIter = dict.Begin(); while (dictIter != dict.End()) { // MITK_DEBUG << "Key " << dictIter->first; std::string value; if (itk::ExposeMetaData(dict, dictIter->first, value)) { // MITK_DEBUG << "Value " << value; auto valuePosition = propertyLookup.find(dictIter->first); if (valuePosition != propertyLookup.end()) { std::string propertyKey = valuePosition->second; // MITK_DEBUG << "--> " << propertyKey; image->SetProperty(propertyKey.c_str(), StringProperty::New(value)); } } else { MITK_WARN << "Tag " << dictIter->first << " not read as string as expected. Ignoring..."; } ++dictIter; } // copy imageblockdescriptor as properties image->SetProperty("dicomseriesreader.SOPClass", StringProperty::New(blockInfo.GetSOPClassUIDAsString())); image->SetProperty( "dicomseriesreader.ReaderImplementationLevelString", StringProperty::New(ReaderImplementationLevelToString(blockInfo.GetReaderImplementationLevel()))); image->SetProperty("dicomseriesreader.ReaderImplementationLevel", GenericProperty::New(blockInfo.GetReaderImplementationLevel())); image->SetProperty("dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New(PixelSpacingInterpretationToString(blockInfo.GetPixelSpacingType()))); image->SetProperty("dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New(blockInfo.GetPixelSpacingType())); image->SetProperty("dicomseriesreader.MultiFrameImage", BoolProperty::New(blockInfo.IsMultiFrameImage())); image->SetProperty("dicomseriesreader.GantyTiltCorrected", BoolProperty::New(blockInfo.HasGantryTiltCorrected())); image->SetProperty("dicomseriesreader.3D+t", BoolProperty::New(blockInfo.HasMultipleTimePoints())); } void DicomSeriesReader::FixSpacingInformation(mitk::Image *image, const ImageBlockDescriptor &imageBlockDescriptor) { // spacing provided by ITK/GDCM Vector3D imageSpacing = image->GetGeometry()->GetSpacing(); ScalarType imageSpacingX = imageSpacing[0]; ScalarType imageSpacingY = imageSpacing[1]; // spacing as desired by MITK (preference for "in patient", else "on detector", or "1.0/1.0") ScalarType desiredSpacingX = imageSpacingX; ScalarType desiredSpacingY = imageSpacingY; imageBlockDescriptor.GetDesiredMITKImagePixelSpacing(desiredSpacingX, desiredSpacingY); MITK_DEBUG << "Loaded spacing: " << imageSpacingX << "/" << imageSpacingY; MITK_DEBUG << "Corrected spacing: " << desiredSpacingX << "/" << desiredSpacingY; imageSpacing[0] = desiredSpacingX; imageSpacing[1] = desiredSpacingY; image->GetGeometry()->SetSpacing(imageSpacing); } void DicomSeriesReader::LoadDicom(const StringContainer &filenames, DataNode &node, bool sort, bool load4D, bool correctTilt, UpdateCallBackMethod callback, Image::Pointer preLoadedImageBlock) { mitk::LocaleSwitch localeSwitch("C"); std::locale previousCppLocale(std::cin.getloc()); std::locale l("C"); std::cin.imbue(l); ImageBlockDescriptor imageBlockDescriptor; const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image Position (Patient) const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image Orientation const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP class UID const gdcm::Tag tagModality(0x0008, 0x0060); // modality const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // pixel spacing const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // imager pixel spacing const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames try { Image::Pointer image = preLoadedImageBlock.IsNull() ? Image::New() : preLoadedImageBlock; CallbackCommand *command = callback ? new CallbackCommand(callback) : nullptr; bool initialize_node = false; /* special case for Philips 3D+t ultrasound images */ if (DicomSeriesReader::IsPhilips3DDicom(filenames.front().c_str())) { // TODO what about imageBlockDescriptor? // TODO what about preLoadedImageBlock? ReadPhilips3DDicom(filenames.front().c_str(), image); initialize_node = true; } else { /* default case: assume "normal" image blocks, possibly 3D+t */ bool canLoadAs4D(true); gdcm::Scanner scanner; ScanForSliceInformation(filenames, scanner); // need non-const access for map auto &tagValueMappings = const_cast(scanner.GetMappings()); std::list imageBlocks = SortIntoBlocksFor3DplusT(filenames, tagValueMappings, sort, canLoadAs4D); unsigned int volume_count = imageBlocks.size(); imageBlockDescriptor.SetSeriesInstanceUID( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagSeriesInstanceUID))); imageBlockDescriptor.SetSOPClassUID( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagSOPClassUID))); imageBlockDescriptor.SetModality( DicomSeriesReader::ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagModality))); imageBlockDescriptor.SetNumberOfFrames( ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagNumberOfFrames))); imageBlockDescriptor.SetPixelSpacingInformation( ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagPixelSpacing)), ConstCharStarToString(scanner.GetValue(filenames.front().c_str(), tagImagerPixelSpacing))); GantryTiltInformation tiltInfo; // check possibility of a single slice with many timesteps. In this case, don't check for tilt, no second slice // possible if (!imageBlocks.empty() && imageBlocks.front().size() > 1 && correctTilt) { // check tiltedness here, potentially fixup ITK's loading result by shifting slice contents // check first and last position slice from tags, make some calculations to detect tilt std::string firstFilename(imageBlocks.front().front()); // calculate from first and last slice to minimize rounding errors std::string secondFilename(imageBlocks.front().back()); std::string imagePosition1( ConstCharStarToString(tagValueMappings[firstFilename.c_str()][tagImagePositionPatient])); std::string imageOrientation( ConstCharStarToString(tagValueMappings[firstFilename.c_str()][tagImageOrientation])); std::string imagePosition2( ConstCharStarToString(tagValueMappings[secondFilename.c_str()][tagImagePositionPatient])); bool ignoredConversionError(-42); // hard to get here, no graceful way to react Point3D origin1(DICOMStringToPoint3D(imagePosition1, ignoredConversionError)); Point3D origin2(DICOMStringToPoint3D(imagePosition2, ignoredConversionError)); Vector3D right; right.Fill(0.0); Vector3D up; right.Fill(0.0); // might be down as well, but it is just a name at this point DICOMStringToOrientationVectors(imageOrientation, right, up, ignoredConversionError); tiltInfo = GantryTiltInformation(origin1, origin2, right, up, filenames.size() - 1); correctTilt = tiltInfo.IsSheared() && tiltInfo.IsRegularGantryTilt(); } else { correctTilt = false; // we CANNOT do that } imageBlockDescriptor.SetHasGantryTiltCorrected(correctTilt); if (volume_count == 1 || !canLoadAs4D || !load4D) { DcmIoType::Pointer io; image = MultiplexLoadDICOMByITK( imageBlocks.front(), correctTilt, tiltInfo, io, command, preLoadedImageBlock); // load first 3D block imageBlockDescriptor.AddFiles(imageBlocks.front()); // only the first part is loaded imageBlockDescriptor.SetHasMultipleTimePoints(false); FixSpacingInformation(image, imageBlockDescriptor); CopyMetaDataToImageProperties(imageBlocks.front(), scanner.GetMappings(), io, imageBlockDescriptor, image); initialize_node = true; } else if (volume_count > 1) { imageBlockDescriptor.AddFiles(filenames); // all is loaded imageBlockDescriptor.SetHasMultipleTimePoints(true); DcmIoType::Pointer io; image = MultiplexLoadDICOMByITK4D( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); initialize_node = true; } } if (initialize_node) { // forward some image properties to node node.GetPropertyList()->ConcatenatePropertyList(image->GetPropertyList(), true); std::string patientName = "NoName"; if (node.GetProperty("dicom.patient.PatientsName")) patientName = node.GetProperty("dicom.patient.PatientsName")->GetValueAsString(); node.SetData(image); node.SetName(patientName); std::cin.imbue(previousCppLocale); } MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "DICOM files loaded (from series UID " << imageBlockDescriptor.GetSeriesInstanceUID() << "):"; MITK_DEBUG << " " << imageBlockDescriptor.GetFilenames().size() << " '" << imageBlockDescriptor.GetModality() << "' files (" << imageBlockDescriptor.GetSOPClassUIDAsString() << ") loaded into 1 mitk::Image"; MITK_DEBUG << " multi-frame: " << (imageBlockDescriptor.IsMultiFrameImage() ? "Yes" : "No"); MITK_DEBUG << " reader support: " << ReaderImplementationLevelToString(imageBlockDescriptor.GetReaderImplementationLevel()); MITK_DEBUG << " pixel spacing type: " << PixelSpacingInterpretationToString(imageBlockDescriptor.GetPixelSpacingType()) << " " << image->GetGeometry()->GetSpacing()[0] << "/" << image->GetGeometry()->GetSpacing()[0]; MITK_DEBUG << " gantry tilt corrected: " << (imageBlockDescriptor.HasGantryTiltCorrected() ? "Yes" : "No"); MITK_DEBUG << " 3D+t: " << (imageBlockDescriptor.HasMultipleTimePoints() ? "Yes" : "No"); MITK_DEBUG << "--------------------------------------------------------------------------------"; } catch ( const std::exception &e ) { // reset locale then throw up std::cin.imbue(previousCppLocale); MITK_DEBUG << "Caught exception in DicomSeriesReader::LoadDicom"; throw e; } } void DicomSeriesReader::ScanForSliceInformation(const StringContainer &filenames, gdcm::Scanner &scanner) { const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image position (Patient) scanner.AddTag(tagImagePositionPatient); const gdcm::Tag tagSeriesInstanceUID(0x0020, 0x000e); // Series Instance UID scanner.AddTag(tagSeriesInstanceUID); const gdcm::Tag tagImageOrientation(0x0020, 0x0037); // Image orientation scanner.AddTag(tagImageOrientation); const gdcm::Tag tagSliceLocation(0x0020, 0x1041); // slice location scanner.AddTag(tagSliceLocation); const gdcm::Tag tagInstanceNumber(0x0020, 0x0013); // (image) instance number scanner.AddTag(tagInstanceNumber); const gdcm::Tag tagSOPInstanceNumber(0x0008, 0x0018); // SOP instance number scanner.AddTag(tagSOPInstanceNumber); const gdcm::Tag tagPixelSpacing(0x0028, 0x0030); // Pixel Spacing scanner.AddTag(tagPixelSpacing); const gdcm::Tag tagImagerPixelSpacing(0x0018, 0x1164); // Imager Pixel Spacing scanner.AddTag(tagImagerPixelSpacing); const gdcm::Tag tagModality(0x0008, 0x0060); // Modality scanner.AddTag(tagModality); const gdcm::Tag tagSOPClassUID(0x0008, 0x0016); // SOP Class UID scanner.AddTag(tagSOPClassUID); const gdcm::Tag tagNumberOfFrames(0x0028, 0x0008); // number of frames scanner.AddTag(tagNumberOfFrames); scanner.Scan(filenames); // make available image information for each file } std::list DicomSeriesReader::SortIntoBlocksFor3DplusT( const StringContainer &presortedFilenames, const gdcm::Scanner::MappingType &tagValueMappings, bool /*sort*/, bool &canLoadAs4D) { std::list imageBlocks; // ignore sort request, because most likely re-sorting is now needed due to changes in GetSeries(bug #8022) StringContainer sorted_filenames = DicomSeriesReader::SortSeriesSlices(presortedFilenames); std::string firstPosition; unsigned int numberOfBlocks(0); // number of 3D image blocks static const gdcm::Tag tagImagePositionPatient(0x0020, 0x0032); // Image position (Patient) const gdcm::Tag tagModality(0x0008, 0x0060); // loop files to determine number of image blocks for (StringContainer::const_iterator fileIter = sorted_filenames.begin(); fileIter != sorted_filenames.end(); ++fileIter) { gdcm::Scanner::TagToValue tagToValueMap = tagValueMappings.find(fileIter->c_str())->second; if (tagToValueMap.find(tagImagePositionPatient) == tagToValueMap.end()) { const std::string &modality = tagToValueMap.find(tagModality)->second; if (modality.compare("RTIMAGE ") == 0 || modality.compare("RTIMAGE") == 0) { MITK_WARN << "Modality " << modality << " is not fully supported yet."; numberOfBlocks = 1; break; } else { // we expect to get images w/ missing position information ONLY as separated blocks. assert(presortedFilenames.size() == 1); numberOfBlocks = 1; break; } } std::string position = tagToValueMap.find(tagImagePositionPatient)->second; MITK_DEBUG << " " << *fileIter << " at " << position; if (firstPosition.empty()) { firstPosition = position; } if (position == firstPosition) { ++numberOfBlocks; } else { break; // enough information to know the number of image blocks } } MITK_DEBUG << " ==> Assuming " << numberOfBlocks << " time steps"; if (numberOfBlocks == 0) return imageBlocks; // only possible if called with no files // loop files to sort them into image blocks unsigned int numberOfExpectedSlices(0); for (unsigned int block = 0; block < numberOfBlocks; ++block) { StringContainer filesOfCurrentBlock; for (StringContainer::const_iterator fileIter = sorted_filenames.begin() + block; fileIter != sorted_filenames.end(); // fileIter += numberOfBlocks) // TODO shouldn't this work? give invalid iterators on first attempts ) { filesOfCurrentBlock.push_back(*fileIter); for (unsigned int b = 0; b < numberOfBlocks; ++b) { if (fileIter != sorted_filenames.end()) ++fileIter; } } imageBlocks.push_back(filesOfCurrentBlock); if (block == 0) { numberOfExpectedSlices = filesOfCurrentBlock.size(); } else { if (filesOfCurrentBlock.size() != numberOfExpectedSlices) { MITK_WARN << "DicomSeriesReader expected " << numberOfBlocks << " image blocks of " << numberOfExpectedSlices << " images each. Block " << block << " got " << filesOfCurrentBlock.size() << " instead. Cannot load this as 3D+t"; // TODO implement recovery (load as many slices 3D+t as much // as possible) canLoadAs4D = false; } } } return imageBlocks; } Image::Pointer DicomSeriesReader::MultiplexLoadDICOMByITK(const StringContainer &filenames, bool correctTilt, const GantryTiltInformation &tiltInfo, DcmIoType::Pointer &io, CallbackCommand *command, Image::Pointer preLoadedImageBlock) { io = DcmIoType::New(); io->SetFileName(filenames.front().c_str()); io->ReadImageInformation(); - if (io->GetPixelType() == itk::ImageIOBase::SCALAR) + if (io->GetPixelType() == itk::IOPixelEnum::SCALAR) { return MultiplexLoadDICOMByITKScalar(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); } - else if (io->GetPixelType() == itk::ImageIOBase::RGB) + else if (io->GetPixelType() == itk::IOPixelEnum::RGB) { return MultiplexLoadDICOMByITKRGBPixel(filenames, correctTilt, tiltInfo, io, command, preLoadedImageBlock); } else { return nullptr; } } Image::Pointer DicomSeriesReader::MultiplexLoadDICOMByITK4D(std::list &imageBlocks, ImageBlockDescriptor imageBlockDescriptor, bool correctTilt, const GantryTiltInformation &tiltInfo, DcmIoType::Pointer &io, CallbackCommand *command, Image::Pointer preLoadedImageBlock) { io = DcmIoType::New(); io->SetFileName(imageBlocks.front().front().c_str()); io->ReadImageInformation(); - if (io->GetPixelType() == itk::ImageIOBase::SCALAR) + if (io->GetPixelType() == itk::IOPixelEnum::SCALAR) { return MultiplexLoadDICOMByITK4DScalar( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); } - else if (io->GetPixelType() == itk::ImageIOBase::RGB) + else if (io->GetPixelType() == itk::IOPixelEnum::RGB) { return MultiplexLoadDICOMByITK4DRGBPixel( imageBlocks, imageBlockDescriptor, correctTilt, tiltInfo, io, command, preLoadedImageBlock); } else { return nullptr; } } } // end namespace mitk diff --git a/Modules/DICOM/src/mitkDICOMITKSeriesGDCMReader.cpp b/Modules/DICOM/src/mitkDICOMITKSeriesGDCMReader.cpp index 5bb2224cbd..19a910e832 100644 --- a/Modules/DICOM/src/mitkDICOMITKSeriesGDCMReader.cpp +++ b/Modules/DICOM/src/mitkDICOMITKSeriesGDCMReader.cpp @@ -1,622 +1,622 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 #define ENABLE_TIMING #include #include #include "mitkDICOMITKSeriesGDCMReader.h" #include "mitkITKDICOMSeriesReaderHelper.h" #include "mitkGantryTiltInformation.h" #include "mitkDICOMTagBasedSorter.h" #include "mitkDICOMGDCMTagScanner.h" -itk::MutexLock::Pointer mitk::DICOMITKSeriesGDCMReader::s_LocaleMutex = itk::MutexLock::New(); +std::mutex mitk::DICOMITKSeriesGDCMReader::s_LocaleMutex; mitk::DICOMITKSeriesGDCMReader::DICOMITKSeriesGDCMReader( unsigned int decimalPlacesForOrientation, bool simpleVolumeImport ) : DICOMFileReader() , m_FixTiltByShearing(m_DefaultFixTiltByShearing) , m_SimpleVolumeReading( simpleVolumeImport ) , m_DecimalPlacesForOrientation( decimalPlacesForOrientation ) , m_ExternalCache(false) { this->EnsureMandatorySortersArePresent( decimalPlacesForOrientation, simpleVolumeImport ); } mitk::DICOMITKSeriesGDCMReader::DICOMITKSeriesGDCMReader( const DICOMITKSeriesGDCMReader& other ) : DICOMFileReader( other ) , m_FixTiltByShearing( other.m_FixTiltByShearing) , m_SortingResultInProgress( other.m_SortingResultInProgress ) , m_Sorter( other.m_Sorter ) , m_EquiDistantBlocksSorter( other.m_EquiDistantBlocksSorter->Clone() ) , m_NormalDirectionConsistencySorter( other.m_NormalDirectionConsistencySorter->Clone() ) , m_ReplacedCLocales( other.m_ReplacedCLocales ) , m_ReplacedCinLocales( other.m_ReplacedCinLocales ) , m_DecimalPlacesForOrientation( other.m_DecimalPlacesForOrientation ) , m_TagCache( other.m_TagCache ) , m_ExternalCache(other.m_ExternalCache) { } mitk::DICOMITKSeriesGDCMReader::~DICOMITKSeriesGDCMReader() { } mitk::DICOMITKSeriesGDCMReader& mitk::DICOMITKSeriesGDCMReader:: operator=( const DICOMITKSeriesGDCMReader& other ) { if ( this != &other ) { DICOMFileReader::operator =( other ); this->m_FixTiltByShearing = other.m_FixTiltByShearing; this->m_SortingResultInProgress = other.m_SortingResultInProgress; this->m_Sorter = other.m_Sorter; // TODO should clone the list items this->m_EquiDistantBlocksSorter = other.m_EquiDistantBlocksSorter->Clone(); this->m_NormalDirectionConsistencySorter = other.m_NormalDirectionConsistencySorter->Clone(); this->m_ReplacedCLocales = other.m_ReplacedCLocales; this->m_ReplacedCinLocales = other.m_ReplacedCinLocales; this->m_DecimalPlacesForOrientation = other.m_DecimalPlacesForOrientation; this->m_TagCache = other.m_TagCache; } return *this; } bool mitk::DICOMITKSeriesGDCMReader::operator==( const DICOMFileReader& other ) const { if ( const auto* otherSelf = dynamic_cast( &other ) ) { if ( this->m_FixTiltByShearing == otherSelf->m_FixTiltByShearing && *( this->m_EquiDistantBlocksSorter ) == *( otherSelf->m_EquiDistantBlocksSorter ) && ( fabs( this->m_DecimalPlacesForOrientation - otherSelf->m_DecimalPlacesForOrientation ) < eps ) ) { // test sorters for equality if ( this->m_Sorter.size() != otherSelf->m_Sorter.size() ) return false; auto mySorterIter = this->m_Sorter.cbegin(); auto oSorterIter = otherSelf->m_Sorter.cbegin(); for ( ; mySorterIter != this->m_Sorter.cend() && oSorterIter != otherSelf->m_Sorter.cend(); ++mySorterIter, ++oSorterIter ) { if ( !( **mySorterIter == **oSorterIter ) ) return false; // this sorter differs } // nothing differs ==> all is equal return true; } else { return false; } } else { return false; } } void mitk::DICOMITKSeriesGDCMReader::SetFixTiltByShearing( bool on ) { this->Modified(); m_FixTiltByShearing = on; } bool mitk::DICOMITKSeriesGDCMReader::GetFixTiltByShearing() const { return m_FixTiltByShearing; } void mitk::DICOMITKSeriesGDCMReader::SetAcceptTwoSlicesGroups( bool accept ) const { this->Modified(); m_EquiDistantBlocksSorter->SetAcceptTwoSlicesGroups( accept ); } bool mitk::DICOMITKSeriesGDCMReader::GetAcceptTwoSlicesGroups() const { return m_EquiDistantBlocksSorter->GetAcceptTwoSlicesGroups(); } void mitk::DICOMITKSeriesGDCMReader::InternalPrintConfiguration( std::ostream& os ) const { unsigned int sortIndex( 1 ); for ( auto sorterIter = m_Sorter.cbegin(); sorterIter != m_Sorter.cend(); ++sortIndex, ++sorterIter ) { os << "Sorting step " << sortIndex << ":" << std::endl; ( *sorterIter )->PrintConfiguration( os, " " ); } os << "Sorting step " << sortIndex << ":" << std::endl; m_EquiDistantBlocksSorter->PrintConfiguration( os, " " ); } std::string mitk::DICOMITKSeriesGDCMReader::GetActiveLocale() { return setlocale( LC_NUMERIC, nullptr ); } void mitk::DICOMITKSeriesGDCMReader::PushLocale() const { - s_LocaleMutex->Lock(); + s_LocaleMutex.lock(); std::string currentCLocale = setlocale( LC_NUMERIC, nullptr ); m_ReplacedCLocales.push( currentCLocale ); setlocale( LC_NUMERIC, "C" ); std::locale currentCinLocale( std::cin.getloc() ); m_ReplacedCinLocales.push( currentCinLocale ); std::locale l( "C" ); std::cin.imbue( l ); - s_LocaleMutex->Unlock(); + s_LocaleMutex.unlock(); } void mitk::DICOMITKSeriesGDCMReader::PopLocale() const { - s_LocaleMutex->Lock(); + s_LocaleMutex.lock(); if ( !m_ReplacedCLocales.empty() ) { setlocale( LC_NUMERIC, m_ReplacedCLocales.top().c_str() ); m_ReplacedCLocales.pop(); } else { MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader."; } if ( !m_ReplacedCinLocales.empty() ) { std::cin.imbue( m_ReplacedCinLocales.top() ); m_ReplacedCinLocales.pop(); } else { MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader."; } - s_LocaleMutex->Unlock(); + s_LocaleMutex.unlock(); } mitk::DICOMITKSeriesGDCMReader::SortingBlockList mitk::DICOMITKSeriesGDCMReader::Condense3DBlocks( SortingBlockList& input ) { return input; // to be implemented differently by sub-classes } #if defined( MBILOG_ENABLE_DEBUG ) || defined( ENABLE_TIMING ) #define timeStart( part ) timer.Start( part ); #define timeStop( part ) timer.Stop( part ); #else #define timeStart( part ) #define timeStop( part ) #endif void mitk::DICOMITKSeriesGDCMReader::AnalyzeInputFiles() { itk::TimeProbesCollectorBase timer; timeStart( "Reset" ); this->ClearOutputs(); timeStop( "Reset" ); // prepare initial sorting (== list of input files) const StringList inputFilenames = this->GetInputFiles(); timeStart( "Check input for DCM" ); if ( inputFilenames.empty() || !this->CanHandleFile( inputFilenames.front() ) // first || !this->CanHandleFile( inputFilenames.back() ) // last || !this->CanHandleFile( inputFilenames[inputFilenames.size() / 2] ) // roughly central file ) { // TODO a read-as-many-as-possible fallback could be implemented here MITK_DEBUG << "Reader unable to process files.."; return; } timeStop( "Check input for DCM" ); // scan files for sorting-relevant tags if ( m_TagCache.IsNull() || ( m_TagCache->GetMTime()GetMTime() && !m_ExternalCache )) { timeStart( "Tag scanning" ); DICOMGDCMTagScanner::Pointer filescanner = DICOMGDCMTagScanner::New(); filescanner->SetInputFiles( inputFilenames ); filescanner->AddTagPaths( this->GetTagsOfInterest() ); PushLocale(); filescanner->Scan(); PopLocale(); m_TagCache = filescanner->GetScanCache(); // keep alive and make accessible to sub-classes timeStop("Tag scanning"); } else { // ensure that the tag cache contains our required tags AND files and has scanned! } m_SortingResultInProgress.clear(); m_SortingResultInProgress.push_back(m_TagCache->GetFrameInfoList()); // sort and split blocks as configured timeStart( "Sorting frames" ); unsigned int sorterIndex = 0; for ( auto sorterIter = m_Sorter.cbegin(); sorterIter != m_Sorter.cend(); ++sorterIndex, ++sorterIter ) { std::stringstream ss; ss << "Sorting step " << sorterIndex; timeStart( ss.str().c_str() ); m_SortingResultInProgress = this->InternalExecuteSortingStep( sorterIndex, *sorterIter, m_SortingResultInProgress ); timeStop( ss.str().c_str() ); } if ( !m_SimpleVolumeReading ) { // a last extra-sorting step: ensure equidistant slices timeStart( "EquiDistantBlocksSorter" ); m_SortingResultInProgress = this->InternalExecuteSortingStep( sorterIndex++, m_EquiDistantBlocksSorter.GetPointer(), m_SortingResultInProgress ); timeStop( "EquiDistantBlocksSorter" ); } timeStop( "Sorting frames" ); timeStart( "Condensing 3D blocks" ); m_SortingResultInProgress = this->Condense3DBlocks( m_SortingResultInProgress ); timeStop( "Condensing 3D blocks" ); // provide final result as output timeStart( "Output" ); unsigned int o = this->GetNumberOfOutputs(); this->SetNumberOfOutputs( o + m_SortingResultInProgress.size() ); // Condense3DBlocks may already have added outputs! for ( auto blockIter = m_SortingResultInProgress.cbegin(); blockIter != m_SortingResultInProgress.cend(); ++o, ++blockIter ) { const DICOMDatasetAccessingImageFrameList& gdcmFrameInfoList = *blockIter; assert( !gdcmFrameInfoList.empty() ); // reverse frames if necessary // update tilt information from absolute last sorting const DICOMDatasetList datasetList = ConvertToDICOMDatasetList( gdcmFrameInfoList ); m_NormalDirectionConsistencySorter->SetInput( datasetList ); m_NormalDirectionConsistencySorter->Sort(); const DICOMDatasetAccessingImageFrameList sortedGdcmInfoFrameList = ConvertToDICOMDatasetAccessingImageFrameList( m_NormalDirectionConsistencySorter->GetOutput( 0 ) ); const GantryTiltInformation& tiltInfo = m_NormalDirectionConsistencySorter->GetTiltInformation(); // set frame list for current block const DICOMImageFrameList frameList = ConvertToDICOMImageFrameList( sortedGdcmInfoFrameList ); assert( !frameList.empty() ); DICOMImageBlockDescriptor block; block.SetTagCache( this->GetTagCache() ); // important: this must be before SetImageFrameList(), because // SetImageFrameList will trigger reading of lots of interesting // tags! block.SetAdditionalTagsOfInterest( GetAdditionalTagsOfInterest() ); block.SetTagLookupTableToPropertyFunctor( GetTagLookupTableToPropertyFunctor() ); block.SetImageFrameList( frameList ); block.SetTiltInformation( tiltInfo ); block.SetReaderImplementationLevel( this->GetReaderImplementationLevel( block.GetSOPClassUID() ) ); this->SetOutput( o, block ); } timeStop( "Output" ); #if defined( MBILOG_ENABLE_DEBUG ) || defined( ENABLE_TIMING ) std::cout << "---------------------------------------------------------------" << std::endl; timer.Report( std::cout ); std::cout << "---------------------------------------------------------------" << std::endl; #endif } mitk::DICOMITKSeriesGDCMReader::SortingBlockList mitk::DICOMITKSeriesGDCMReader::InternalExecuteSortingStep( unsigned int sortingStepIndex, const DICOMDatasetSorter::Pointer& sorter, const SortingBlockList& input ) { SortingBlockList nextStepSorting; // we should not modify our input list while processing it std::stringstream ss; ss << "Sorting step " << sortingStepIndex << " '"; #if defined( MBILOG_ENABLE_DEBUG ) sorter->PrintConfiguration( ss ); #endif ss << "'"; nextStepSorting.clear(); MITK_DEBUG << "================================================================================"; MITK_DEBUG << "DICOMITKSeriesGDCMReader: " << ss.str() << ": " << input.size() << " groups input"; unsigned int groupIndex = 0; for ( auto blockIter = input.cbegin(); blockIter != input.cend(); ++groupIndex, ++blockIter ) { const DICOMDatasetAccessingImageFrameList& gdcmInfoFrameList = *blockIter; const DICOMDatasetList datasetList = ConvertToDICOMDatasetList( gdcmInfoFrameList ); #if defined( MBILOG_ENABLE_DEBUG ) MITK_DEBUG << "--------------------------------------------------------------------------------"; MITK_DEBUG << "DICOMITKSeriesGDCMReader: " << ss.str() << ", dataset group " << groupIndex << " (" << datasetList.size() << " datasets): "; for ( auto oi = datasetList.cbegin(); oi != datasetList.cend(); ++oi ) { MITK_DEBUG << " INPUT : " << ( *oi )->GetFilenameIfAvailable(); } #endif sorter->SetInput( datasetList ); sorter->Sort(); unsigned int numberOfResultingBlocks = sorter->GetNumberOfOutputs(); for ( unsigned int b = 0; b < numberOfResultingBlocks; ++b ) { const DICOMDatasetList blockResult = sorter->GetOutput( b ); for ( auto oi = blockResult.cbegin(); oi != blockResult.cend(); ++oi ) { MITK_DEBUG << " OUTPUT(" << b << ") :" << ( *oi )->GetFilenameIfAvailable(); } DICOMDatasetAccessingImageFrameList sortedGdcmInfoFrameList = ConvertToDICOMDatasetAccessingImageFrameList( blockResult ); nextStepSorting.push_back( sortedGdcmInfoFrameList ); } } return nextStepSorting; } mitk::ReaderImplementationLevel mitk::DICOMITKSeriesGDCMReader::GetReaderImplementationLevel( const std::string sopClassUID ) { if ( sopClassUID.empty() ) { return SOPClassUnknown; } gdcm::UIDs uidKnowledge; uidKnowledge.SetFromUID( sopClassUID.c_str() ); gdcm::UIDs::TSName gdcmType = static_cast((gdcm::UIDs::TSType)uidKnowledge); switch ( gdcmType ) { case gdcm::UIDs::CTImageStorage: case gdcm::UIDs::MRImageStorage: case gdcm::UIDs::PositronEmissionTomographyImageStorage: case gdcm::UIDs::ComputedRadiographyImageStorage: case gdcm::UIDs::DigitalXRayImageStorageForPresentation: case gdcm::UIDs::DigitalXRayImageStorageForProcessing: return SOPClassSupported; case gdcm::UIDs::NuclearMedicineImageStorage: return SOPClassPartlySupported; case gdcm::UIDs::SecondaryCaptureImageStorage: return SOPClassImplemented; default: return SOPClassUnsupported; } } // void AllocateOutputImages(); bool mitk::DICOMITKSeriesGDCMReader::LoadImages() { bool success = true; unsigned int numberOfOutputs = this->GetNumberOfOutputs(); for ( unsigned int o = 0; o < numberOfOutputs; ++o ) { success &= this->LoadMitkImageForOutput( o ); } return success; } bool mitk::DICOMITKSeriesGDCMReader::LoadMitkImageForImageBlockDescriptor( DICOMImageBlockDescriptor& block ) const { PushLocale(); const DICOMImageFrameList& frames = block.GetImageFrameList(); const GantryTiltInformation tiltInfo = block.GetTiltInformation(); bool hasTilt = tiltInfo.IsRegularGantryTilt(); ITKDICOMSeriesReaderHelper::StringContainer filenames; filenames.reserve( frames.size() ); for ( auto frameIter = frames.cbegin(); frameIter != frames.cend(); ++frameIter ) { filenames.push_back( ( *frameIter )->Filename ); } mitk::ITKDICOMSeriesReaderHelper helper; bool success( true ); try { mitk::Image::Pointer mitkImage = helper.Load( filenames, m_FixTiltByShearing && hasTilt, tiltInfo ); block.SetMitkImage( mitkImage ); } catch ( const std::exception& e ) { success = false; MITK_ERROR << "Exception during image loading: " << e.what(); } PopLocale(); return success; } bool mitk::DICOMITKSeriesGDCMReader::LoadMitkImageForOutput( unsigned int o ) { DICOMImageBlockDescriptor& block = this->InternalGetOutput( o ); return this->LoadMitkImageForImageBlockDescriptor( block ); } bool mitk::DICOMITKSeriesGDCMReader::CanHandleFile( const std::string& filename ) { return ITKDICOMSeriesReaderHelper::CanHandleFile( filename ); } void mitk::DICOMITKSeriesGDCMReader::AddSortingElement( DICOMDatasetSorter* sorter, bool atFront ) { assert( sorter ); if ( atFront ) { m_Sorter.push_front( sorter ); } else { m_Sorter.push_back( sorter ); } this->Modified(); } mitk::DICOMITKSeriesGDCMReader::ConstSorterList mitk::DICOMITKSeriesGDCMReader::GetFreelyConfiguredSortingElements() const { std::list result; unsigned int sortIndex( 0 ); for ( auto sorterIter = m_Sorter.begin(); sorterIter != m_Sorter.end(); ++sortIndex, ++sorterIter ) { if ( sortIndex > 0 ) // ignore first element (see EnsureMandatorySortersArePresent) { result.push_back( ( *sorterIter ).GetPointer() ); } } return result; } void mitk::DICOMITKSeriesGDCMReader::EnsureMandatorySortersArePresent( unsigned int decimalPlacesForOrientation, bool simpleVolumeImport ) { DICOMTagBasedSorter::Pointer splitter = DICOMTagBasedSorter::New(); splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0010) ); // Number of Rows splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0011) ); // Number of Columns splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0030) ); // Pixel Spacing splitter->AddDistinguishingTag( DICOMTag(0x0018, 0x1164) ); // Imager Pixel Spacing splitter->AddDistinguishingTag( DICOMTag(0x0020, 0x0037), new mitk::DICOMTagBasedSorter::CutDecimalPlaces(decimalPlacesForOrientation) ); // Image Orientation (Patient) splitter->AddDistinguishingTag( DICOMTag(0x0018, 0x0050) ); // Slice Thickness if ( simpleVolumeImport ) { MITK_DEBUG << "Simple volume reading: ignoring number of frames"; } else { splitter->AddDistinguishingTag( DICOMTag(0x0028, 0x0008) ); // Number of Frames } this->AddSortingElement( splitter, true ); // true = at front if ( m_EquiDistantBlocksSorter.IsNull() ) { m_EquiDistantBlocksSorter = mitk::EquiDistantBlocksSorter::New(); } m_EquiDistantBlocksSorter->SetAcceptTilt( m_FixTiltByShearing ); if ( m_NormalDirectionConsistencySorter.IsNull() ) { m_NormalDirectionConsistencySorter = mitk::NormalDirectionConsistencySorter::New(); } } void mitk::DICOMITKSeriesGDCMReader::SetToleratedOriginOffsetToAdaptive( double fractionOfInterSliceDistance ) const { assert( m_EquiDistantBlocksSorter.IsNotNull() ); m_EquiDistantBlocksSorter->SetToleratedOriginOffsetToAdaptive( fractionOfInterSliceDistance ); this->Modified(); } void mitk::DICOMITKSeriesGDCMReader::SetToleratedOriginOffset( double millimeters ) const { assert( m_EquiDistantBlocksSorter.IsNotNull() ); m_EquiDistantBlocksSorter->SetToleratedOriginOffset( millimeters ); this->Modified(); } double mitk::DICOMITKSeriesGDCMReader::GetToleratedOriginError() const { assert( m_EquiDistantBlocksSorter.IsNotNull() ); return m_EquiDistantBlocksSorter->GetToleratedOriginOffset(); } bool mitk::DICOMITKSeriesGDCMReader::IsToleratedOriginOffsetAbsolute() const { assert( m_EquiDistantBlocksSorter.IsNotNull() ); return m_EquiDistantBlocksSorter->IsToleratedOriginOffsetAbsolute(); } double mitk::DICOMITKSeriesGDCMReader::GetDecimalPlacesForOrientation() const { return m_DecimalPlacesForOrientation; } mitk::DICOMTagCache::Pointer mitk::DICOMITKSeriesGDCMReader::GetTagCache() const { return m_TagCache; } void mitk::DICOMITKSeriesGDCMReader::SetTagCache( const DICOMTagCache::Pointer& tagCache ) { m_TagCache = tagCache; m_ExternalCache = tagCache.IsNotNull(); } mitk::DICOMTagPathList mitk::DICOMITKSeriesGDCMReader::GetTagsOfInterest() const { DICOMTagPathList completeList; // check all configured sorters for ( auto sorterIter = m_Sorter.cbegin(); sorterIter != m_Sorter.cend(); ++sorterIter ) { assert( sorterIter->IsNotNull() ); const DICOMTagList tags = ( *sorterIter )->GetTagsOfInterest(); completeList.insert( completeList.end(), tags.cbegin(), tags.cend() ); } // check our own forced sorters DICOMTagList tags = m_EquiDistantBlocksSorter->GetTagsOfInterest(); completeList.insert( completeList.end(), tags.cbegin(), tags.cend() ); tags = m_NormalDirectionConsistencySorter->GetTagsOfInterest(); completeList.insert( completeList.end(), tags.cbegin(), tags.cend() ); // add the tags for DICOMImageBlockDescriptor tags = DICOMImageBlockDescriptor::GetTagsOfInterest(); completeList.insert( completeList.end(), tags.cbegin(), tags.cend() ); const AdditionalTagsMapType tagList = GetAdditionalTagsOfInterest(); for ( auto iter = tagList.cbegin(); iter != tagList.cend(); ++iter ) { completeList.push_back( iter->first ) ; } return completeList; } diff --git a/Modules/DICOM/src/mitkDICOMTagScanner.cpp b/Modules/DICOM/src/mitkDICOMTagScanner.cpp index 908ba5134f..7b2852eb85 100644 --- a/Modules/DICOM/src/mitkDICOMTagScanner.cpp +++ b/Modules/DICOM/src/mitkDICOMTagScanner.cpp @@ -1,72 +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. ============================================================================*/ #include "mitkDICOMTagScanner.h" -itk::MutexLock::Pointer mitk::DICOMTagScanner::s_LocaleMutex = itk::MutexLock::New(); +std::mutex mitk::DICOMTagScanner::s_LocaleMutex; mitk::DICOMTagScanner::DICOMTagScanner() { } mitk::DICOMTagScanner::~DICOMTagScanner() { } void mitk::DICOMTagScanner::PushLocale() const { - s_LocaleMutex->Lock(); + s_LocaleMutex.lock(); std::string currentCLocale = setlocale(LC_NUMERIC, nullptr); m_ReplacedCLocales.push(currentCLocale); setlocale(LC_NUMERIC, "C"); std::locale currentCinLocale(std::cin.getloc()); m_ReplacedCinLocales.push(currentCinLocale); std::locale l("C"); std::cin.imbue(l); - s_LocaleMutex->Unlock(); + s_LocaleMutex.unlock(); } void mitk::DICOMTagScanner::PopLocale() const { - s_LocaleMutex->Lock(); + s_LocaleMutex.lock(); if (!m_ReplacedCLocales.empty()) { setlocale(LC_NUMERIC, m_ReplacedCLocales.top().c_str()); m_ReplacedCLocales.pop(); } else { MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader."; } if (!m_ReplacedCinLocales.empty()) { std::cin.imbue(m_ReplacedCinLocales.top()); m_ReplacedCinLocales.pop(); } else { MITK_WARN << "Mismatched PopLocale on DICOMITKSeriesGDCMReader."; } - s_LocaleMutex->Unlock(); + s_LocaleMutex.unlock(); } std::string mitk::DICOMTagScanner::GetActiveLocale() { return setlocale(LC_NUMERIC, nullptr); } diff --git a/Modules/DICOM/src/mitkITKDICOMSeriesReaderHelper.cpp b/Modules/DICOM/src/mitkITKDICOMSeriesReaderHelper.cpp index 47e1ca4b63..5ac21da5d4 100644 --- a/Modules/DICOM/src/mitkITKDICOMSeriesReaderHelper.cpp +++ b/Modules/DICOM/src/mitkITKDICOMSeriesReaderHelper.cpp @@ -1,455 +1,455 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 #include #define BOOST_DATE_TIME_NO_LIB //Prevent unnecessary/unwanted auto link in this compilation when activating boost libraries in the MITK superbuild //It is necessary because BOOST_ALL_DYN_LINK overwrites BOOST_DATE_TIME_NO_LIB #if defined(BOOST_ALL_DYN_LINK) #undef BOOST_ALL_DYN_LINK #endif #include #include "mitkITKDICOMSeriesReaderHelper.h" #include "mitkITKDICOMSeriesReaderHelper.txx" #include "mitkDICOMGDCMTagScanner.h" #include "mitkArbitraryTimeGeometry.h" #include "dcmtk/dcmdata/dcvrda.h" const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::AcquisitionDateTag = mitk::DICOMTag( 0x0008, 0x0022 ); const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::AcquisitionTimeTag = mitk::DICOMTag( 0x0008, 0x0032 ); const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::TriggerTimeTag = mitk::DICOMTag( 0x0018, 0x1060 ); #define switch3DCase( IOType, T ) \ case IOType: \ return LoadDICOMByITK( filenames, correctTilt, tiltInfo, io ); bool mitk::ITKDICOMSeriesReaderHelper::CanHandleFile( const std::string& filename ) { MITK_DEBUG << "ITKDICOMSeriesReaderHelper::CanHandleFile " << filename; itk::GDCMImageIO::Pointer tester = itk::GDCMImageIO::New(); return tester->CanReadFile( filename.c_str() ); } mitk::Image::Pointer mitk::ITKDICOMSeriesReaderHelper::Load( const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo ) { if ( filenames.empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; return nullptr; // this is not actually an error but the result is very simple } typedef itk::GDCMImageIO DcmIoType; DcmIoType::Pointer io = DcmIoType::New(); try { if ( io->CanReadFile( filenames.front().c_str() ) ) { io->SetFileName( filenames.front().c_str() ); io->ReadImageInformation(); - if ( io->GetPixelType() == itk::ImageIOBase::SCALAR ) + if ( io->GetPixelType() == itk::IOPixelEnum::SCALAR ) { switch ( io->GetComponentType() ) { - switch3DCase(DcmIoType::UCHAR, unsigned char) switch3DCase(DcmIoType::CHAR, char) switch3DCase( - DcmIoType::USHORT, unsigned short) switch3DCase(DcmIoType::SHORT, short) - switch3DCase(DcmIoType::UINT, unsigned int) switch3DCase(DcmIoType::INT, int) switch3DCase( - DcmIoType::ULONG, long unsigned int) switch3DCase(DcmIoType::LONG, long int) - switch3DCase(DcmIoType::FLOAT, float) switch3DCase(DcmIoType::DOUBLE, double) default + switch3DCase(itk::IOComponentEnum::UCHAR, unsigned char) switch3DCase(itk::IOComponentEnum::CHAR, char) switch3DCase( + itk::IOComponentEnum::USHORT, unsigned short) switch3DCase(itk::IOComponentEnum::SHORT, short) + switch3DCase(itk::IOComponentEnum::UINT, unsigned int) switch3DCase(itk::IOComponentEnum::INT, int) switch3DCase( + itk::IOComponentEnum::ULONG, long unsigned int) switch3DCase(itk::IOComponentEnum::LONG, long int) + switch3DCase(itk::IOComponentEnum::FLOAT, float) switch3DCase(itk::IOComponentEnum::DOUBLE, double) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } - else if ( io->GetPixelType() == itk::ImageIOBase::RGB ) + else if ( io->GetPixelType() == itk::IOPixelEnum::RGB ) { switch ( io->GetComponentType() ) { - switch3DCase(DcmIoType::UCHAR, itk::RGBPixel) switch3DCase( - DcmIoType::CHAR, itk::RGBPixel) switch3DCase(DcmIoType::USHORT, + switch3DCase(itk::IOComponentEnum::UCHAR, itk::RGBPixel) switch3DCase( + itk::IOComponentEnum::CHAR, itk::RGBPixel) switch3DCase(itk::IOComponentEnum::USHORT, itk::RGBPixel) - switch3DCase(DcmIoType::SHORT, itk::RGBPixel) switch3DCase( - DcmIoType::UINT, itk::RGBPixel) switch3DCase(DcmIoType::INT, itk::RGBPixel) - switch3DCase(DcmIoType::ULONG, itk::RGBPixel) - switch3DCase(DcmIoType::LONG, itk::RGBPixel) switch3DCase( - DcmIoType::FLOAT, itk::RGBPixel) switch3DCase(DcmIoType::DOUBLE, + switch3DCase(itk::IOComponentEnum::SHORT, itk::RGBPixel) switch3DCase( + itk::IOComponentEnum::UINT, itk::RGBPixel) switch3DCase(itk::IOComponentEnum::INT, itk::RGBPixel) + switch3DCase(itk::IOComponentEnum::ULONG, itk::RGBPixel) + switch3DCase(itk::IOComponentEnum::LONG, itk::RGBPixel) switch3DCase( + itk::IOComponentEnum::FLOAT, itk::RGBPixel) switch3DCase(itk::IOComponentEnum::DOUBLE, itk::RGBPixel) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } MITK_ERROR << "Unsupported DICOM pixel type"; return nullptr; } } catch ( const itk::MemoryAllocationError& e ) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch ( const std::exception& e ) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch ( ... ) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return nullptr; } #define switch3DnTCase( IOType, T ) \ case IOType: \ return LoadDICOMByITK3DnT( filenamesLists, correctTilt, tiltInfo, io ); mitk::Image::Pointer mitk::ITKDICOMSeriesReaderHelper::Load3DnT( const StringContainerList& filenamesLists, bool correctTilt, const GantryTiltInformation& tiltInfo ) { if ( filenamesLists.empty() || filenamesLists.front().empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; return nullptr; // this is not actually an error but the result is very simple } typedef itk::GDCMImageIO DcmIoType; DcmIoType::Pointer io = DcmIoType::New(); try { if ( io->CanReadFile( filenamesLists.front().front().c_str() ) ) { io->SetFileName( filenamesLists.front().front().c_str() ); io->ReadImageInformation(); - if ( io->GetPixelType() == itk::ImageIOBase::SCALAR ) + if ( io->GetPixelType() == itk::IOPixelEnum::SCALAR ) { switch ( io->GetComponentType() ) { - switch3DnTCase(DcmIoType::UCHAR, unsigned char) switch3DnTCase(DcmIoType::CHAR, char) - switch3DnTCase(DcmIoType::USHORT, unsigned short) switch3DnTCase( - DcmIoType::SHORT, short) switch3DnTCase(DcmIoType::UINT, - unsigned int) switch3DnTCase(DcmIoType::INT, int) - switch3DnTCase(DcmIoType::ULONG, long unsigned int) switch3DnTCase(DcmIoType::LONG, long int) - switch3DnTCase(DcmIoType::FLOAT, float) switch3DnTCase(DcmIoType::DOUBLE, double) default + switch3DnTCase(itk::IOComponentEnum::UCHAR, unsigned char) switch3DnTCase(itk::IOComponentEnum::CHAR, char) + switch3DnTCase(itk::IOComponentEnum::USHORT, unsigned short) switch3DnTCase( + itk::IOComponentEnum::SHORT, short) switch3DnTCase(itk::IOComponentEnum::UINT, + unsigned int) switch3DnTCase(itk::IOComponentEnum::INT, int) + switch3DnTCase(itk::IOComponentEnum::ULONG, long unsigned int) switch3DnTCase(itk::IOComponentEnum::LONG, long int) + switch3DnTCase(itk::IOComponentEnum::FLOAT, float) switch3DnTCase(itk::IOComponentEnum::DOUBLE, double) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } - else if ( io->GetPixelType() == itk::ImageIOBase::RGB ) + else if ( io->GetPixelType() == itk::IOPixelEnum::RGB ) { switch ( io->GetComponentType() ) { - switch3DnTCase(DcmIoType::UCHAR, itk::RGBPixel) - switch3DnTCase(DcmIoType::CHAR, itk::RGBPixel) switch3DnTCase( - DcmIoType::USHORT, itk::RGBPixel) switch3DnTCase(DcmIoType::SHORT, + switch3DnTCase(itk::IOComponentEnum::UCHAR, itk::RGBPixel) + switch3DnTCase(itk::IOComponentEnum::CHAR, itk::RGBPixel) switch3DnTCase( + itk::IOComponentEnum::USHORT, itk::RGBPixel) switch3DnTCase(itk::IOComponentEnum::SHORT, itk::RGBPixel) - switch3DnTCase(DcmIoType::UINT, itk::RGBPixel) switch3DnTCase( - DcmIoType::INT, itk::RGBPixel) switch3DnTCase(DcmIoType::ULONG, + switch3DnTCase(itk::IOComponentEnum::UINT, itk::RGBPixel) switch3DnTCase( + itk::IOComponentEnum::INT, itk::RGBPixel) switch3DnTCase(itk::IOComponentEnum::ULONG, itk::RGBPixel) - switch3DnTCase(DcmIoType::LONG, itk::RGBPixel) switch3DnTCase( - DcmIoType::FLOAT, itk::RGBPixel) switch3DnTCase(DcmIoType::DOUBLE, + switch3DnTCase(itk::IOComponentEnum::LONG, itk::RGBPixel) switch3DnTCase( + itk::IOComponentEnum::FLOAT, itk::RGBPixel) switch3DnTCase(itk::IOComponentEnum::DOUBLE, itk::RGBPixel) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } MITK_ERROR << "Unsupported DICOM pixel type"; return nullptr; } } catch ( const itk::MemoryAllocationError& e ) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch ( const std::exception& e ) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch ( ... ) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return nullptr; } bool ConvertDICOMDateTimeString( const std::string& dateString, const std::string& timeString, OFDateTime& time ) { OFString content( timeString.c_str() ); if ( !dateString.empty() ) { content = OFString( dateString.c_str() ).append( content ); } else { // This is a workaround for DICOM data that has an AquisitionTime but no AquisitionDate. // In this case, we use the current date. That's not really nice, but is absolutely OK // as we're only interested in the time anyways... OFString currentDate; DcmDate::getCurrentDate( currentDate ); content = currentDate.append( content ); } const OFCondition result = DcmDateTime::getOFDateTimeFromString( content, time ); return result.good(); } boost::posix_time::ptime ConvertOFDateTimeToPTime( const OFDateTime& time ) { const boost::gregorian::date boostDate( time.getDate().getYear(), time.getDate().getMonth(), time.getDate().getDay() ); const boost::posix_time::time_duration boostTime = boost::posix_time::hours( time.getTime().getHour() ) + boost::posix_time::minutes( time.getTime().getMinute() ) + boost::posix_time::seconds( static_cast(time.getTime().getSecond()) ) + boost::posix_time::milliseconds( time.getTime().getMilliSecond() ); boost::posix_time::ptime result( boostDate, boostTime ); return result; } OFDateTime GetLowerDateTime( const OFDateTime& time1, const OFDateTime& time2 ) { OFDateTime result = time1; if ( ( time2.getDate() < time1.getDate() ) || ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() < time1.getTime() ) ) ) { result = time2; } return result; } OFDateTime GetUpperDateTime( const OFDateTime& time1, const OFDateTime& time2 ) { OFDateTime result = time1; if ( ( time2.getDate() > time1.getDate() ) || ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() > time1.getTime() ) ) ) { result = time2; } return result; } double ComputeMiliSecDuration( const OFDateTime& start, const OFDateTime& stop ) { const boost::posix_time::ptime startTime = ConvertOFDateTimeToPTime( start ); const boost::posix_time::ptime stopTime = ConvertOFDateTimeToPTime( stop ); ::boost::posix_time::time_duration duration = stopTime - startTime; return duration.total_milliseconds(); } bool mitk::ITKDICOMSeriesReaderHelper::ExtractDateTimeBoundsAndTriggerOfTimeStep( const StringContainer& filenamesOfTimeStep, DateTimeBounds& bounds, TimeBounds& triggerBounds) { DICOMGDCMTagScanner::Pointer filescanner = DICOMGDCMTagScanner::New(); filescanner->SetInputFiles(filenamesOfTimeStep); filescanner->AddTag(AcquisitionDateTag); filescanner->AddTag(AcquisitionTimeTag); filescanner->AddTag(TriggerTimeTag); filescanner->Scan(); const DICOMDatasetAccessingImageFrameList frameList = filescanner->GetFrameInfoList(); bool result = false; bool firstAq = true; bool firstTr = true; triggerBounds = TimeBounds(0.0); for (auto pos = frameList.cbegin(); pos != frameList.cend(); ++pos) { const std::string aqDateStr = (*pos)->GetTagValueAsString(AcquisitionDateTag).value; const std::string aqTimeStr = (*pos)->GetTagValueAsString(AcquisitionTimeTag).value; const std::string triggerTimeStr = (*pos)->GetTagValueAsString(TriggerTimeTag).value; OFDateTime aqDateTime; const bool convertAqResult = ConvertDICOMDateTimeString(aqDateStr, aqTimeStr, aqDateTime); OFBool convertTriggerResult; mitk::ScalarType triggerTime = OFStandard::atof(triggerTimeStr.c_str(), &convertTriggerResult); if (convertAqResult) { if (firstAq) { bounds[0] = aqDateTime; bounds[1] = aqDateTime; firstAq = false; } else { bounds[0] = GetLowerDateTime(bounds[0], aqDateTime); bounds[1] = GetUpperDateTime(bounds[1], aqDateTime); } result = true; } if (convertTriggerResult) { if (firstTr) { triggerBounds[0] = triggerTime; triggerBounds[1] = triggerTime; firstTr = false; } else { triggerBounds[0] = std::min(triggerBounds[0], triggerTime); triggerBounds[1] = std::max(triggerBounds[1], triggerTime); } result = true; } } return result; }; bool mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeStep( const StringContainer& filenamesOfTimeStep, TimeBounds& bounds, const OFDateTime& baselineDateTime ) { DateTimeBounds aqDTBounds; TimeBounds triggerBounds; bool result = ExtractDateTimeBoundsAndTriggerOfTimeStep(filenamesOfTimeStep, aqDTBounds, triggerBounds); mitk::ScalarType lowerBound = ComputeMiliSecDuration( baselineDateTime, aqDTBounds[0] ); mitk::ScalarType upperBound = ComputeMiliSecDuration( baselineDateTime, aqDTBounds[1] ); if ( lowerBound < mitk::eps || upperBound < mitk::eps ) { lowerBound = triggerBounds[0]; upperBound = triggerBounds[1]; } bounds[0] = lowerBound; bounds[1] = upperBound; return result; }; mitk::ITKDICOMSeriesReaderHelper::TimeBoundsList mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeSteps( const StringContainerList& filenamesOfTimeSteps ) { TimeBoundsList result; OFDateTime baseLine; // extract the timebounds DateTimeBounds baselineDateTimeBounds; TimeBounds triggerBounds; auto pos = filenamesOfTimeSteps.cbegin(); ExtractDateTimeBoundsAndTriggerOfTimeStep(*pos, baselineDateTimeBounds, triggerBounds); baseLine = baselineDateTimeBounds[0]; // timebounds for baseline is 0 TimeBounds bounds( 0.0 ); result.push_back( bounds ); // iterate over the remaining timesteps for ( ++pos; pos != filenamesOfTimeSteps.cend(); ++pos ) { TimeBounds bounds( 0.0 ); TimeBounds dateTimeBounds; // extract the timebounds relative to the baseline if ( ExtractTimeBoundsOfTimeStep( *pos, dateTimeBounds, baseLine ) ) { bounds[0] = dateTimeBounds[0]; bounds[1] = dateTimeBounds[1]; } result.push_back( bounds ); } return result; }; mitk::TimeGeometry::Pointer mitk::ITKDICOMSeriesReaderHelper::GenerateTimeGeometry( const BaseGeometry* templateGeometry, const TimeBoundsList& boundsList ) { TimeGeometry::Pointer timeGeometry; double check = 0.0; const auto boundListSize = boundsList.size(); for ( std::size_t pos = 0; pos < boundListSize; ++pos ) { check += boundsList[pos][0]; check += boundsList[pos][1]; } if ( check < mitk::eps ) { // if all bounds are zero we assume that the bounds could not be correctly determined // and as a fallback generate a time geometry in the old mitk style ProportionalTimeGeometry::Pointer newTimeGeometry = ProportionalTimeGeometry::New(); newTimeGeometry->Initialize( templateGeometry, boundListSize ); timeGeometry = newTimeGeometry.GetPointer(); } else { ArbitraryTimeGeometry::Pointer newTimeGeometry = ArbitraryTimeGeometry::New(); newTimeGeometry->ClearAllGeometries(); newTimeGeometry->ReserveSpaceForGeometries( boundListSize ); for ( std::size_t pos = 0; pos < boundListSize; ++pos ) { TimeBounds bounds = boundsList[pos]; if ( pos + 1 < boundListSize ) { //Currently we do not explicitly support "gaps" in the time coverage //thus we set the max time bound of a time step to the min time bound //of its successor. bounds[1] = boundsList[pos + 1][0]; } newTimeGeometry->AppendNewTimeStepClone(templateGeometry, bounds[0], bounds[1]); } timeGeometry = newTimeGeometry.GetPointer(); } return timeGeometry; }; diff --git a/Modules/DICOMTesting/include/mitkTestDICOMLoading.h b/Modules/DICOMTesting/include/mitkTestDICOMLoading.h index 63289abc0b..ad041a45a6 100644 --- a/Modules/DICOMTesting/include/mitkTestDICOMLoading.h +++ b/Modules/DICOMTesting/include/mitkTestDICOMLoading.h @@ -1,111 +1,111 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef 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 ); + std::string ComponentTypeToString( itk::IOComponentEnum 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/mitkTestDICOMLoading.cpp b/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp index 9d63c65aa9..d6a44162f7 100644 --- a/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp +++ b/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp @@ -1,585 +1,585 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkArbitraryTimeGeometry.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) +mitk::TestDICOMLoading::ComponentTypeToString(itk::IOComponentEnum type) { - if (type == itk::ImageIOBase::UCHAR) + if (type == itk::IOComponentEnum::UCHAR) return "UCHAR"; - else if (type == itk::ImageIOBase::CHAR) + else if (type == itk::IOComponentEnum::CHAR) return "CHAR"; - else if (type == itk::ImageIOBase::USHORT) + else if (type == itk::IOComponentEnum::USHORT) return "USHORT"; - else if (type == itk::ImageIOBase::SHORT) + else if (type == itk::IOComponentEnum::SHORT) return "SHORT"; - else if (type == itk::ImageIOBase::UINT) + else if (type == itk::IOComponentEnum::UINT) return "UINT"; - else if (type == itk::ImageIOBase::INT) + else if (type == itk::IOComponentEnum::INT) return "INT"; - else if (type == itk::ImageIOBase::ULONG) + else if (type == itk::IOComponentEnum::ULONG) return "ULONG"; - else if (type == itk::ImageIOBase::LONG) + else if (type == itk::IOComponentEnum::LONG) return "LONG"; - else if (type == itk::ImageIOBase::FLOAT) + else if (type == itk::IOComponentEnum::FLOAT) return "FLOAT"; - else if (type == itk::ImageIOBase::DOUBLE) + else if (type == itk::IOComponentEnum::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: "; /////////////////////////////////////// // Workarround T27883. See https://phabricator.mitk.org/T27883#219473 for more details. // This workarround should be removed as soon as T28262 is solved! TimeBounds timeBounds = timeGeometry->GetTimeBounds(); auto atg = dynamic_cast(timeGeometry); if (atg && atg->HasCollapsedFinalTimeStep()) { timeBounds[1] = timeBounds[1] - 1.; } //Original code: //const TimeBounds timeBounds = timeGeometry->GetTimeBounds(); // // End of workarround for T27883 ////////////////////////////////////// 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; 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"); } } 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 (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 (!surroundingKeys.empty()) { surroundingKeys.pop(); // last of same length } 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/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h b/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h index 5774512c17..a1b875db38 100644 --- a/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h +++ b/Modules/DataTypesExt/include/mitkAffineBaseDataInteractor3D.h @@ -1,105 +1,105 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkAffineBaseDataInteractor3D_h #define mitkAffineBaseDataInteractor3D_h #include "MitkDataTypesExtExports.h" #include #include namespace mitk { ////create events for interactions #pragma GCC visibility push(default) - itkEventMacro(AffineInteractionEvent, itk::AnyEvent); - itkEventMacro(ScaleEvent, AffineInteractionEvent); - itkEventMacro(RotateEvent, AffineInteractionEvent); - itkEventMacro(TranslateEvent, AffineInteractionEvent); + itkEventMacroDeclaration(AffineInteractionEvent, itk::AnyEvent); + itkEventMacroDeclaration(ScaleEvent, AffineInteractionEvent); + itkEventMacroDeclaration(RotateEvent, AffineInteractionEvent); + itkEventMacroDeclaration(TranslateEvent, AffineInteractionEvent); #pragma GCC visibility pop /** * \brief Affine interaction with mitk::BaseGeometry. * * \ingroup Interaction */ // Inherit from DataInteractor, this provides functionality of a state machine and configurable inputs. class MITKDATATYPESEXT_EXPORT AffineBaseDataInteractor3D : public DataInteractor { public: mitkClassMacro(AffineBaseDataInteractor3D, DataInteractor); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void SetDataNode(DataNode *node) override; void TranslateGeometry(mitk::Vector3D translate, mitk::BaseGeometry *geometry); void RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry *geometry); void ScaleGeometry(mitk::Point3D newScale, mitk::BaseGeometry *geometry); mitk::BaseGeometry *GetUpdatedTimeGeometry(mitk::InteractionEvent *interactionEvent); protected: AffineBaseDataInteractor3D(); ~AffineBaseDataInteractor3D() override; /** * Here actions strings from the loaded state machine pattern are mapped to functions of * the DataInteractor. These functions are called when an action from the state machine pattern is executed. */ void ConnectActionsAndFunctions() override; /** * This function is called when a DataNode has been set/changed. */ void DataNodeChanged() override; /** * Initializes the movement, stores starting position. */ virtual bool CheckOverObject(const InteractionEvent *); virtual void SelectObject(StateMachineAction *, InteractionEvent *); virtual void DeselectObject(StateMachineAction *, InteractionEvent *); virtual void InitTranslate(StateMachineAction *, InteractionEvent *); virtual void InitRotate(StateMachineAction *, InteractionEvent *); virtual void TranslateObject(StateMachineAction *, InteractionEvent *); virtual void RotateObject(StateMachineAction *, InteractionEvent *); virtual void ScaleObject(StateMachineAction *, InteractionEvent *); virtual void TranslateUpKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void TranslateDownKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void TranslateLeftKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void TranslateRightKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void TranslateUpModifierKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void TranslateDownModifierKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void RotateUpKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void RotateDownKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void RotateLeftKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void RotateRightKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void RotateUpModifierKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void RotateDownModifierKey(StateMachineAction *, InteractionEvent *interactionEvent); virtual void ScaleDownKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent); virtual void ScaleUpKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent); virtual void RestoreNodeProperties(); /** * @brief InitMembers convinience method to avoid code duplication between InitRotate() and InitTranslate(). * @param interactionEvent */ bool InitMembers(InteractionEvent *interactionEvent); private: Point3D m_InitialPickedWorldPoint; Point2D m_InitialPickedDisplayPoint; Geometry3D::Pointer m_OriginalGeometry; }; } #endif diff --git a/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp b/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp index 43bb1944cc..b33a752ab7 100644 --- a/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp +++ b/Modules/DataTypesExt/src/mitkAffineBaseDataInteractor3D.cpp @@ -1,504 +1,512 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkAffineBaseDataInteractor3D.h" #include #include #include #include #include #include #include #include #include #include #include #include +namespace mitk +{ + itkEventMacroDefinition(AffineInteractionEvent, itk::AnyEvent); + itkEventMacroDefinition(ScaleEvent, AffineInteractionEvent); + itkEventMacroDefinition(RotateEvent, AffineInteractionEvent); + itkEventMacroDefinition(TranslateEvent, AffineInteractionEvent); +} + // Properties to allow the user to interact with the base data const char *translationStepSizePropertyName = "AffineBaseDataInteractor3D.Translation Step Size"; const char *selectedColorPropertyName = "AffineBaseDataInteractor3D.Selected Color"; const char *deselectedColorPropertyName = "AffineBaseDataInteractor3D.Deselected Color"; const char *priorPropertyName = "AffineBaseDataInteractor3D.Prior Color"; const char *rotationStepSizePropertyName = "AffineBaseDataInteractor3D.Rotation Step Size"; const char *scaleStepSizePropertyName = "AffineBaseDataInteractor3D.Scale Step Size"; const char *anchorPointX = "AffineBaseDataInteractor3D.Anchor Point X"; const char *anchorPointY = "AffineBaseDataInteractor3D.Anchor Point Y"; const char *anchorPointZ = "AffineBaseDataInteractor3D.Anchor Point Z"; mitk::AffineBaseDataInteractor3D::AffineBaseDataInteractor3D() { m_OriginalGeometry = mitk::Geometry3D::New(); } mitk::AffineBaseDataInteractor3D::~AffineBaseDataInteractor3D() { this->RestoreNodeProperties(); } void mitk::AffineBaseDataInteractor3D::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually // executing an action CONNECT_CONDITION("isOverObject", CheckOverObject); // **Function** in the statmachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject", SelectObject); CONNECT_FUNCTION("deselectObject", DeselectObject); CONNECT_FUNCTION("initTranslate", InitTranslate); CONNECT_FUNCTION("initRotate", InitRotate); CONNECT_FUNCTION("translateObject", TranslateObject); CONNECT_FUNCTION("rotateObject", RotateObject); CONNECT_FUNCTION("scaleObject", ScaleObject); CONNECT_FUNCTION("translateUpKey", TranslateUpKey); CONNECT_FUNCTION("translateDownKey", TranslateDownKey); CONNECT_FUNCTION("translateLeftKey", TranslateLeftKey); CONNECT_FUNCTION("translateRightKey", TranslateRightKey); CONNECT_FUNCTION("translateUpModifierKey", TranslateUpModifierKey); CONNECT_FUNCTION("translateDownModifierKey", TranslateDownModifierKey); CONNECT_FUNCTION("scaleDownKey", ScaleDownKey); CONNECT_FUNCTION("scaleUpKey", ScaleUpKey); CONNECT_FUNCTION("rotateUpKey", RotateUpKey); CONNECT_FUNCTION("rotateDownKey", RotateDownKey); CONNECT_FUNCTION("rotateLeftKey", RotateLeftKey); CONNECT_FUNCTION("rotateRightKey", RotateRightKey); CONNECT_FUNCTION("rotateUpModifierKey", RotateUpModifierKey); CONNECT_FUNCTION("rotateDownModifierKey", RotateDownModifierKey); } void mitk::AffineBaseDataInteractor3D::TranslateUpKey(StateMachineAction *, InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(2, stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::TranslateDownKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(2, -stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::TranslateLeftKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(0, -stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::TranslateRightKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(0, stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::TranslateUpModifierKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(1, stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::TranslateDownModifierKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(translationStepSizePropertyName, stepSize); mitk::Vector3D movementVector; movementVector.Fill(0.0); movementVector.SetElement(1, -stepSize); this->TranslateGeometry(movementVector, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::RotateUpKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(-stepSize, 0, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::RotateDownKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(stepSize, 0, this->GetUpdatedTimeGeometry(interactionEvent)); return; } void mitk::AffineBaseDataInteractor3D::RotateLeftKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(-stepSize, 2, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::RotateRightKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(stepSize, 2, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::RotateUpModifierKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(stepSize, 1, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::RotateDownModifierKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 1.0f; this->GetDataNode()->GetFloatProperty(rotationStepSizePropertyName, stepSize); this->RotateGeometry(-stepSize, 1, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::ScaleUpKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 0.1f; this->GetDataNode()->GetFloatProperty(scaleStepSizePropertyName, stepSize); mitk::Point3D newScale; newScale.Fill(stepSize); this->ScaleGeometry(newScale, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::ScaleDownKey(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { float stepSize = 0.1f; this->GetDataNode()->GetFloatProperty(scaleStepSizePropertyName, stepSize); mitk::Point3D newScale; newScale.Fill(-stepSize); this->ScaleGeometry(newScale, this->GetUpdatedTimeGeometry(interactionEvent)); } void mitk::AffineBaseDataInteractor3D::ScaleGeometry(mitk::Point3D newScale, mitk::BaseGeometry *geometry) { mitk::Point3D anchorPoint; float pointX = 0.0f; float pointY = 0.0f; float pointZ = 0.0f; anchorPoint.Fill(0.0); this->GetDataNode()->GetFloatProperty(anchorPointX, pointX); this->GetDataNode()->GetFloatProperty(anchorPointY, pointY); this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ); anchorPoint[0] = pointX; anchorPoint[1] = pointY; anchorPoint[2] = pointZ; auto *doOp = new mitk::ScaleOperation(OpSCALE, newScale, anchorPoint); geometry->ExecuteOperation(doOp); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::AffineBaseDataInteractor3D::RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry *geometry) { mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis); float pointX = 0.0f; float pointY = 0.0f; float pointZ = 0.0f; mitk::Point3D pointOfRotation; pointOfRotation.Fill(0.0); this->GetDataNode()->GetFloatProperty(anchorPointX, pointX); this->GetDataNode()->GetFloatProperty(anchorPointY, pointY); this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ); pointOfRotation[0] = pointX; pointOfRotation[1] = pointY; pointOfRotation[2] = pointZ; auto *doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle); geometry->ExecuteOperation(doOp); delete doOp; mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::AffineBaseDataInteractor3D::TranslateGeometry(mitk::Vector3D translate, mitk::BaseGeometry *geometry) { geometry->Translate(translate); this->GetDataNode()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } mitk::BaseGeometry *mitk::AffineBaseDataInteractor3D::GetUpdatedTimeGeometry(mitk::InteractionEvent *interactionEvent) { // Get the correct time geometry to support 3D + t int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); BaseGeometry *geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); if (geometry == nullptr) MITK_ERROR << "Geometry is nullptr. Cannot modify it."; return geometry; } void mitk::AffineBaseDataInteractor3D::DataNodeChanged() { mitk::DataNode::Pointer newInputNode = this->GetDataNode(); if (newInputNode.IsNotNull()) { // add default properties newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0)); newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(0.0, 0.0, 1.0)); newInputNode->AddProperty(translationStepSizePropertyName, mitk::FloatProperty::New(1.0f)); newInputNode->AddProperty(rotationStepSizePropertyName, mitk::FloatProperty::New(1.0f)); newInputNode->AddProperty(scaleStepSizePropertyName, mitk::FloatProperty::New(0.1f)); // save the previous color of the node, in order to restore it after the interactor is destroyed mitk::ColorProperty::Pointer priorColor = dynamic_cast(newInputNode->GetProperty("color")); if (priorColor.IsNotNull()) { mitk::ColorProperty::Pointer tmpCopyOfPriorColor = mitk::ColorProperty::New(); tmpCopyOfPriorColor->SetColor(priorColor->GetColor()); newInputNode->AddProperty(priorPropertyName, tmpCopyOfPriorColor); } newInputNode->SetColor(0.0, 0.0, 1.0); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::AffineBaseDataInteractor3D::SetDataNode(DataNode *node) { this->RestoreNodeProperties(); // if there was another node set, restore it's color DataInteractor::SetDataNode(node); } bool mitk::AffineBaseDataInteractor3D::CheckOverObject(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; Point3D currentWorldPoint; if (interactionEvent->GetSender()->PickObject(positionEvent->GetPointerPositionOnScreen(), currentWorldPoint) == this->GetDataNode()) return true; return false; } void mitk::AffineBaseDataInteractor3D::SelectObject(StateMachineAction *, InteractionEvent *) { DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(selectedColorPropertyName)); if (selectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", selectedColor); } RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::AffineBaseDataInteractor3D::DeselectObject(StateMachineAction *, InteractionEvent *) { DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", selectedColor); } RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::AffineBaseDataInteractor3D::InitTranslate(StateMachineAction *, InteractionEvent *interactionEvent) { InitMembers(interactionEvent); } void mitk::AffineBaseDataInteractor3D::InitRotate(StateMachineAction *, InteractionEvent *interactionEvent) { InitMembers(interactionEvent); } bool mitk::AffineBaseDataInteractor3D::InitMembers(InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; m_InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); m_InitialPickedWorldPoint = positionEvent->GetPositionInWorld(); // Get the timestep to also support 3D+t int timeStep = 0; if ((interactionEvent->GetSender()) != nullptr) timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); // Make deep copy of current Geometry3D of the plane this->GetDataNode()->GetData()->UpdateOutputInformation(); // make sure that the Geometry is up-to-date m_OriginalGeometry = static_cast(this->GetDataNode()->GetData()->GetGeometry(timeStep)->Clone().GetPointer()); return true; } void mitk::AffineBaseDataInteractor3D::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; Point3D currentPickedPoint = positionEvent->GetPositionInWorld(); Vector3D interactionMove; interactionMove[0] = currentPickedPoint[0] - m_InitialPickedWorldPoint[0]; interactionMove[1] = currentPickedPoint[1] - m_InitialPickedWorldPoint[1]; interactionMove[2] = currentPickedPoint[2] - m_InitialPickedWorldPoint[2]; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); geometry->SetOrigin(m_OriginalGeometry->GetOrigin()); this->TranslateGeometry(interactionMove, this->GetUpdatedTimeGeometry(interactionEvent)); return; } void mitk::AffineBaseDataInteractor3D::RotateObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return; Point2D currentPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); Point3D currentWorldPoint = positionEvent->GetPositionInWorld(); vtkCamera *camera = nullptr; vtkRenderer *currentVtkRenderer = nullptr; if ((interactionEvent->GetSender()) != nullptr) { camera = interactionEvent->GetSender()->GetVtkRenderer()->GetActiveCamera(); currentVtkRenderer = interactionEvent->GetSender()->GetVtkRenderer(); } if (camera && currentVtkRenderer) { double vpn[3]; camera->GetViewPlaneNormal(vpn); Vector3D viewPlaneNormal; viewPlaneNormal[0] = vpn[0]; viewPlaneNormal[1] = vpn[1]; viewPlaneNormal[2] = vpn[2]; Vector3D interactionMove; interactionMove[0] = currentWorldPoint[0] - m_InitialPickedWorldPoint[0]; interactionMove[1] = currentWorldPoint[1] - m_InitialPickedWorldPoint[1]; interactionMove[2] = currentWorldPoint[2] - m_InitialPickedWorldPoint[2]; if (interactionMove[0] == 0 && interactionMove[1] == 0 && interactionMove[2] == 0) return; Vector3D rotationAxis = itk::CrossProduct(viewPlaneNormal, interactionMove); rotationAxis.Normalize(); int *size = currentVtkRenderer->GetSize(); double l2 = (currentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) * (currentPickedDisplayPoint[0] - m_InitialPickedDisplayPoint[0]) + (currentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]) * (currentPickedDisplayPoint[1] - m_InitialPickedDisplayPoint[1]); double rotationAngle = 360.0 * sqrt(l2 / (size[0] * size[0] + size[1] * size[1])); // Use center of data bounding box as center of rotation Point3D rotationCenter = m_OriginalGeometry->GetCenter(); int timeStep = 0; if ((interactionEvent->GetSender()) != nullptr) timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); // Reset current Geometry3D to original state (pre-interaction) and // apply rotation RotationOperation op(OpROTATE, rotationCenter, rotationAxis, rotationAngle); Geometry3D::Pointer newGeometry = static_cast(m_OriginalGeometry->Clone().GetPointer()); newGeometry->ExecuteOperation(&op); mitk::TimeGeometry::Pointer timeGeometry = this->GetDataNode()->GetData()->GetTimeGeometry(); if (timeGeometry.IsNotNull()) timeGeometry->SetTimeStepGeometry(newGeometry, timeStep); RenderingManager::GetInstance()->RequestUpdateAll(); } } void mitk::AffineBaseDataInteractor3D::ScaleObject(StateMachineAction *, InteractionEvent * /*interactionEvent*/) { return; } void mitk::AffineBaseDataInteractor3D::RestoreNodeProperties() { mitk::DataNode::Pointer inputNode = this->GetDataNode(); if (inputNode.IsNull()) return; mitk::ColorProperty::Pointer color = dynamic_cast(inputNode->GetProperty(priorPropertyName)); if (color.IsNotNull()) { inputNode->GetPropertyList()->SetProperty("color", color); } inputNode->GetPropertyList()->DeleteProperty(selectedColorPropertyName); inputNode->GetPropertyList()->DeleteProperty(deselectedColorPropertyName); inputNode->GetPropertyList()->DeleteProperty(priorPropertyName); inputNode->GetPropertyList()->DeleteProperty(translationStepSizePropertyName); inputNode->GetPropertyList()->DeleteProperty(rotationStepSizePropertyName); inputNode->GetPropertyList()->DeleteProperty(scaleStepSizePropertyName); // update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } diff --git a/Modules/IGT/Algorithms/mitkNavigationDataToPointSetFilter.cpp b/Modules/IGT/Algorithms/mitkNavigationDataToPointSetFilter.cpp index 98b562bfa9..19bc8ffa15 100644 --- a/Modules/IGT/Algorithms/mitkNavigationDataToPointSetFilter.cpp +++ b/Modules/IGT/Algorithms/mitkNavigationDataToPointSetFilter.cpp @@ -1,247 +1,247 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkNavigationDataToPointSetFilter.h" #include #include #include mitk::NavigationDataToPointSetFilter::NavigationDataToPointSetFilter() { mitk::PointSet::Pointer output = mitk::PointSet::New(); this->SetNumberOfRequiredOutputs(1); this->SetNthOutput(0, output.GetPointer()); this->SetNumberOfRequiredInputs(1); m_OperationMode = Mode3D; m_CurrentTimeStep = 0; m_RingBufferSize = 50; //the default ring buffer size m_NumberForMean = 100; } mitk::NavigationDataToPointSetFilter::~NavigationDataToPointSetFilter() { } void mitk::NavigationDataToPointSetFilter::GenerateData() { switch (m_OperationMode) { case Mode3D: GenerateDataMode3D(); break; case Mode3DMean: GenerateDataMode3DMean(); break; case Mode4D: GenerateDataMode4D(); break; default: break; } } void mitk::NavigationDataToPointSetFilter::SetInput(const NavigationData* nd) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(0, const_cast(nd)); this->CreateOutputsForAllInputs(); } void mitk::NavigationDataToPointSetFilter::SetInput(unsigned int idx, const NavigationData* nd) { // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(idx, const_cast(nd)); this->CreateOutputsForAllInputs(); } const mitk::NavigationData* mitk::NavigationDataToPointSetFilter::GetInput( void ) { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(0)); } const mitk::NavigationData* mitk::NavigationDataToPointSetFilter::GetInput( unsigned int idx ) { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(idx)); } void mitk::NavigationDataToPointSetFilter::CreateOutputsForAllInputs() { switch (m_OperationMode) { case Mode3D: this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs()); // create one pointset output for each navigation data input break; case Mode3DMean: this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs()); // create one pointset output for each navigation data input break; case Mode4D: this->SetNumberOfIndexedOutputs(1); // create just one output pointset that will contain all input navigation data objects break; default: break; } for (unsigned int idx = 0; idx < this->GetNumberOfIndexedOutputs(); ++idx) { if (this->GetOutput(idx) == nullptr) { DataObjectPointer newOutput = this->MakeOutput(idx); this->SetNthOutput(idx, newOutput); } } this->Modified(); } void mitk::NavigationDataToPointSetFilter::GenerateDataMode3D() { for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs() ; ++i) // for each output PointSet { mitk::PointSet* output = this->GetOutput(i); assert(output); const mitk::NavigationData* input = this->GetInput(i); assert(input); if (input->IsDataValid() == false) // don't add point if input is invalid continue; mitk::PointSet::PointType pos = input->GetPosition(); // NavigationData::PositionType must be compatible with PointSet::PointType! output->InsertPoint(output->GetSize(), pos); // add point with current position of input NavigationData to the output PointSet // \TODO: regard ringbuffersize } } /** * @brief read n times all connected inputs and sum them into outputs. Finish with dividing each output by n. **/ void mitk::NavigationDataToPointSetFilter::GenerateDataMode3DMean() { //make it editable through a Set method if needed //check for outputs and inputs for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs() ; ++i) // for each output PointSet; change through pointsets to collect all navigation data in order { assert(this->GetOutput(i)); assert(this->GetInput(i)); } //vector of counters for each output std::vector counterVec(this->GetNumberOfIndexedOutputs(),0); //vector of old timesteps for each output std::vector vectorOldTime(this->GetNumberOfIndexedOutputs()); //use first Output to get the size of the pointsets. All output pointssets have to have the same size! mitk::PointSet::PointIdentifier newPointId = this->GetOutput()->GetSize(); bool numberForMean_is_reached = false; while (!numberForMean_is_reached) { for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs() ; ++i) // for each output PointSet; change through pointsets to collect all navigation data in order { mitk::PointSet* output = this->GetOutput(i); const mitk::NavigationData* input = this->GetInput(i); if (input->IsDataValid() == false) // don't add point if input is invalid continue;//do not store mitk::PointSet::PointType pos; if (counterVec[i] == 0) //first Element must be inserted { vectorOldTime[i] = input->GetIGTTimeStamp(); //no need to call an update pos = input->GetPosition(); // NavigationData::PositionType must be compatible with PointSet::PointType! output->InsertPoint(newPointId, pos); // add point with current position of input NavigationData to the output PointSet counterVec[i]++; } else { //manually call an update to track new positions this->ProcessObject::GetInput(i)->Update(); input = this->GetInput(i); mitk::NavigationData::TimeStampType newTime = input->GetIGTTimeStamp(); if (vectorOldTime[i]GetPosition(); // NavigationData::PositionType must be compatible with PointSet::PointType! //calculate the summ of the old position and the current coordinate - mitk::Vector3D vec; - vec.SetVnlVector(pos.GetVnlVector()); + mitk::Vector3D vec(0.0); + vec.SetVnlVector(pos.GetVnlVector().as_ref()); mitk::PointSet::PointType oPoint = output->GetPoint(newPointId); oPoint += vec;//summ up output->SetPoint(newPointId, oPoint); //store in counterVec to know how many have been added (and not skipped because of invalid data) counterVec[i]++; vectorOldTime[i] = newTime; } } // \TODO: regard ringbuffersize } numberForMean_is_reached = true; for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs() ; ++i) { if (counterVec[i]GetNumberOfIndexedOutputs() ; ++i) // for each output PointSet; change through pointsets to collect all navigation data in order { mitk::PointSet* output = this->GetOutput(i); mitk::PointSet::PointType oPoint = output->GetPoint(newPointId); for (unsigned int index = 0; index < oPoint.Size(); index++) oPoint[index] = oPoint[index] / counterVec[i]; output->SetPoint(newPointId, oPoint); MBI_INFO << "For output # " << i << ", " << counterVec[i] << " tracked positions used for averaging"; } } void mitk::NavigationDataToPointSetFilter::GenerateDataMode4D() { mitk::PointSet* output = this->GetOutput(); assert(output); for (unsigned int index = 0; index < this->GetNumberOfIndexedInputs(); index++) { const mitk::NavigationData* nd = GetInput(index); assert(nd); mitk::NavigationData::PositionType point = nd->GetPosition(); //get the position output->SetPoint( index, point, m_CurrentTimeStep); //store it in the pointset always at the current time step } if (m_CurrentTimeStep == m_RingBufferSize - 1) // update ring buffer index m_CurrentTimeStep = 0; else m_CurrentTimeStep++; } void mitk::NavigationDataToPointSetFilter::SetOperationMode( OperationMode mode ) { m_OperationMode = mode; //Initialize 4D Mode if (m_OperationMode == Mode4D) m_CurrentTimeStep = 0; this->Modified(); this->CreateOutputsForAllInputs(); } diff --git a/Modules/IGT/Algorithms/mitkPivotCalibration.cpp b/Modules/IGT/Algorithms/mitkPivotCalibration.cpp index 47a70d0f7a..765d1488cd 100644 --- a/Modules/IGT/Algorithms/mitkPivotCalibration.cpp +++ b/Modules/IGT/Algorithms/mitkPivotCalibration.cpp @@ -1,110 +1,110 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkPivotCalibration.h" #include "vnl/algo/vnl_svd.h" #include "vnl/vnl_matrix.h" #include "vnl/vnl_vector.h" #include mitk::PivotCalibration::PivotCalibration() : m_NavigationDatas(std::vector()), m_ResultPivotPoint(mitk::Point3D(0.0)) { } mitk::PivotCalibration::~PivotCalibration() { } void mitk::PivotCalibration::AddNavigationData(mitk::NavigationData::Pointer data) { m_NavigationDatas.push_back(data); } bool mitk::PivotCalibration::ComputePivotResult() { return ComputePivotPoint(); } bool mitk::PivotCalibration::ComputePivotPoint() { double defaultThreshold = 1e-1; std::vector _CheckedTransforms; for (size_t i = 0; i < m_NavigationDatas.size(); ++i) { if (!m_NavigationDatas.at(i)->IsDataValid()) { MITK_WARN << "Skipping invalid transform " << i << "."; continue; } _CheckedTransforms.push_back(m_NavigationDatas.at(i)); } if (_CheckedTransforms.empty()) { MITK_WARN << "Checked Transforms are empty"; return false; } unsigned int rows = 3 * _CheckedTransforms.size(); unsigned int columns = 6; vnl_matrix< double > A(rows, columns), minusI(3, 3, 0), R(3, 3); vnl_vector< double > b(rows), x(columns), t(3); minusI(0, 0) = -1; minusI(1, 1) = -1; minusI(2, 2) = -1; //do the computation and set the internal variables unsigned int currentRow = 0; for (size_t i = 0; i < _CheckedTransforms.size(); ++i) { t = _CheckedTransforms.at(i)->GetPosition().GetVnlVector();// t = the current position of the tracked sensor t *= -1; b.update(t, currentRow); //b = combines the position for each collected transform in one column vector - R = _CheckedTransforms.at(i)->GetOrientation().rotation_matrix_transpose().transpose(); // R = the current rotation of the tracked sensor, *rotation_matrix_transpose().transpose() is used to obtain original matrix + R = _CheckedTransforms.at(i)->GetOrientation().rotation_matrix_transpose().transpose().as_ref(); // R = the current rotation of the tracked sensor, *rotation_matrix_transpose().transpose() is used to obtain original matrix A.update(R, currentRow, 0); //A = the matrix which stores the rotations for each collected transform and -I A.update(minusI, currentRow, 3); currentRow += 3; } vnl_svd svdA(A); //The singular value decomposition of matrix A svdA.zero_out_absolute(defaultThreshold); //there is a solution only if rank(A)=6 (columns are linearly //independent) if (svdA.rank() < 6) { MITK_WARN << "svdA.rank() < 6"; return false; } else { x = svdA.solve(b); //x = the resulting pivot point m_ResultRMSError = (A * x - b).rms(); //the root mean sqaure error of the computation //sets the Pivot Point m_ResultPivotPoint[0] = x[0]; m_ResultPivotPoint[1] = x[1]; m_ResultPivotPoint[2] = x[2]; } return true; } diff --git a/Modules/IGT/IO/mitkNavigationDataPlayer.h b/Modules/IGT/IO/mitkNavigationDataPlayer.h index ff629034dc..61e2170214 100644 --- a/Modules/IGT/IO/mitkNavigationDataPlayer.h +++ b/Modules/IGT/IO/mitkNavigationDataPlayer.h @@ -1,99 +1,97 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKNavigationDataPlayer_H_HEADER_INCLUDED_ #define MITKNavigationDataPlayer_H_HEADER_INCLUDED_ #include -#include - namespace mitk { /**Documentation * \brief This class is used to play recorded (see mitkNavigationDataRecorder class) NavigationDataSets. * * TODO * * * \ingroup IGT */ class MITKIGT_EXPORT NavigationDataPlayer : public NavigationDataPlayerBase { public: mitkClassMacro(NavigationDataPlayer, NavigationDataPlayerBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); enum PlayerState { PlayerStopped, PlayerRunning, PlayerPaused }; typedef mitk::NavigationData::TimeStampType TimeStampType; /** * \brief Used for pipeline update just to tell the pipeline that we always have to update */ void UpdateOutputInformation() override; /** * \brief This method starts the player. * * The method mitk::NavigationDataPlayer::SetNavigationDataSet() has to be called before. * * @throw mitk::IGTException If m_NavigationDataSet is null. */ void StartPlaying(); /** * \brief Stops the player and closes the stream. * After a call of StopPlaying(), StartPlaying() must be called to get new * output data. */ void StopPlaying(); /** * \brief This method pauses the player. If you want to play again call Resume() */ void Pause(); /** * \brief This method resumes the player when it was paused. */ void Resume(); PlayerState GetCurrentPlayerState(); TimeStampType GetTimeStampSinceStart(); protected: NavigationDataPlayer(); ~NavigationDataPlayer() override; /** * \brief Set outputs to the navigation data object corresponding to current time. */ void GenerateData() override; PlayerState m_CurPlayerState; /** * \brief The start time of the playing. Set in the method mitk::NavigationDataPlayer::StartPlaying(). */ TimeStampType m_StartPlayingTimeStamp; /** * \brief Stores the time when a pause began. */ TimeStampType m_PauseTimeStamp; TimeStampType m_TimeStampSinceStart; }; } // namespace mitk #endif /* MITKNavigationDataPlayer_H_HEADER_INCLUDED_ */ diff --git a/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.cpp b/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.cpp index 19d87f5c38..0f1161ce22 100644 --- a/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.cpp +++ b/Modules/IGT/Rendering/mitkNavigationDataSliceVisualization.cpp @@ -1,219 +1,219 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkNavigationDataSliceVisualization.h" #include "mitkBaseRenderer.h" mitk::NavigationDataSliceVisualization::NavigationDataSliceVisualization() : mitk::NavigationDataToNavigationDataFilter(), m_Renderer(nullptr), m_ViewDirection(Axial) { m_TipOffset[0] = 0.0f; m_TipOffset[1] = 0.0f; m_TipOffset[2] = 0.0f; m_ToolTrajectory[0] = 0; m_ToolTrajectory[1] = 0; m_ToolTrajectory[2] = -1; m_WorldVerticalVector[0] = 0.0; m_WorldVerticalVector[1] = 1.0; m_WorldVerticalVector[2] = 0.0; } void mitk::NavigationDataSliceVisualization::SetToolTrajectory(Vector3D direction) { if (Equal(direction.GetNorm(), 0.0)) { MITK_WARN << "Ignoring invalid direction of projection: " << direction; return; } if (m_ToolTrajectory != direction) { m_ToolTrajectory = direction; this->SetViewDirection(Oblique); this->Modified(); } } void mitk::NavigationDataSliceVisualization::GenerateData() { // check if renderer was set if (m_Renderer.IsNull()) { itkExceptionMacro(<< "Renderer was not properly set"); } /* update outputs with tracking data from tools */ unsigned int numberOfInputs = this->GetNumberOfInputs(); if (numberOfInputs == 0) { return; } for (unsigned int i = 0; i < numberOfInputs ; ++i) { NavigationData* output = this->GetOutput(i); assert(output); const NavigationData* input = this->GetInput(i); assert(input); if (!input->IsDataValid()) continue; output->Graft(input); // First, copy all information from input to output } // Nothing left to do if we don't have an input with valid data if (numberOfInputs == 0 || !this->GetInput()->IsDataValid()) return; // get position from NavigationData to move the slice to this position Point3D slicePosition = this->GetInput()->GetPosition(); { NavigationData::OrientationType orientation = this->GetInput()->GetOrientation(); Vector3D transformedTipOffset; - transformedTipOffset.SetVnlVector(orientation.rotate(m_TipOffset.GetVnlVector())); + transformedTipOffset.SetVnlVector(orientation.rotate(m_TipOffset.GetVnlVector()).as_ref()); slicePosition += transformedTipOffset; mitk::SliceNavigationController::Pointer snc = m_Renderer->GetSliceNavigationController(); if (Axial == m_ViewDirection) { snc->SetViewDirection(mitk::SliceNavigationController::Axial); snc->SelectSliceByPoint(slicePosition); } else if (Sagittal == m_ViewDirection) { snc->SetViewDirection(mitk::SliceNavigationController::Sagittal); snc->SelectSliceByPoint(slicePosition); } else if (Frontal == m_ViewDirection) { snc->SetViewDirection(mitk::SliceNavigationController::Frontal); snc->SelectSliceByPoint(slicePosition); } else if (AxialOblique == m_ViewDirection || SagittalOblique == m_ViewDirection) { const int slicingPlaneXAxis = AxialOblique == m_ViewDirection ? 0 : 2; // The column 0 is the slicing plane's x-axis, column 1 is the slicing plane's y-axis const mitk::PlaneGeometry::TransformType::MatrixType &m = m_Renderer->GetCurrentWorldPlaneGeometry()->GetIndexToWorldTransform()->GetMatrix(); // Rotate the tool trajectory vector into world coordinate frame (assuming // NavigationData has passed through a NavigationDataTransformFilter to // convert it into world coordinate frame) Vector3D slicingPlaneYAxisVector; - slicingPlaneYAxisVector.SetVnlVector(orientation.rotate(m_ToolTrajectory.GetVnlVector())); + slicingPlaneYAxisVector.SetVnlVector(orientation.rotate(m_ToolTrajectory.GetVnlVector()).as_ref()); // Project the tool trajectory onto the plane normal to x-axis of this // oblique slicing. This defines the y-axis ("up") of the oblique slicing // plane slicingPlaneYAxisVector[slicingPlaneXAxis] = 0.0; // Do nothing for ambigous/undefined cases: // - the R-L component of the x-axis is zero (for AxialOblique) // - the S-I component of the x-axis is zero (for SagittalOblique) // - the A-P component of the y-axis is zero if ( m(slicingPlaneXAxis,0) == 0.0 || m(1,1) == 0.0 || (slicingPlaneXAxis != 0 && slicingPlaneYAxisVector[0] == 0.0) || (slicingPlaneXAxis != 1 && slicingPlaneYAxisVector[1] == 0.0) || (slicingPlaneXAxis != 2 && slicingPlaneYAxisVector[2] == 0.0) ) { return; } // Maintain the A-P orientation of the slice's y-axis regardless of what // direction the tool trajectory points /// @todo Use std::signbit if ( (m(1,1) > 0) != (slicingPlaneYAxisVector[1] > 0) ) { slicingPlaneYAxisVector *= -1; } Vector3D slicingPlaneXAxisVector; slicingPlaneXAxisVector.Fill(0.0); // For AxialOblique: maintain the Left/Right direction of the slice's x-axis // For SagittalOblique: maintain the Superior/Inferior direction of the slice's x-axis /// @todo Use std::copysign slicingPlaneXAxisVector[slicingPlaneXAxis] = m(slicingPlaneXAxis,0) > 0 ? 1.0 : -1.0; Point3D origin; FillVector3D(origin, 0.0, 0.0, 0.0); snc->ReorientSlices(origin, slicingPlaneXAxisVector, slicingPlaneYAxisVector); snc->SelectSliceByPoint(slicePosition); } else if (Oblique == m_ViewDirection) { Vector3D slicingPlaneNormalVector; - slicingPlaneNormalVector.SetVnlVector(orientation.rotate(m_ToolTrajectory.GetVnlVector())); + slicingPlaneNormalVector.SetVnlVector(orientation.rotate(m_ToolTrajectory.GetVnlVector()).as_ref()); // The second column of the Index-to-World matrix is the positive y-axis // of the current slicing plane in world coordinates. const mitk::PlaneGeometry::TransformType::MatrixType &m = m_Renderer->GetCurrentWorldPlaneGeometry()->GetIndexToWorldTransform()->GetMatrix(); mitk::Vector3D currentSlicingPlaneUpVector; mitk::FillVector3D(currentSlicingPlaneUpVector, m[0][1], m[1][1], m[2][1]); mitk::Vector3D worldUpVector = m_WorldVerticalVector; if (angle(worldUpVector.GetVnlVector(), currentSlicingPlaneUpVector.GetVnlVector()) > vnl_math::pi_over_2 ) { worldUpVector *= -1; } mitk::PlaneGeometry::Pointer slicingPlane = mitk::PlaneGeometry::New(); Point3D origin; FillVector3D(origin, 0.0, 0.0, 0.0); slicingPlane->InitializePlane(origin, slicingPlaneNormalVector); // Now that we have the direction of WorldVerticalVector chosen to be the // most "up" direction, project it onto the slicing plane to define the // up vector (y-axis) of the reoriented slices mitk::Vector3D slicingPlaneUpVector; if ( slicingPlane->Project(worldUpVector, slicingPlaneUpVector) ) { // slicingPlaneUpVector CROSS slicingPlaneNormalVector -> slicingPlaneRightVector // Math is done in double precision as much as possible to get more // orthogonal right and up vectors which fixes a VNL SVD error when // the WorldGeometry matrix is later inverted itk::Vector slicingPlaneUpVector_double; FillVector3D(slicingPlaneUpVector_double, slicingPlaneUpVector[0], slicingPlaneUpVector[1], slicingPlaneUpVector[2]); itk::Vector slicingPlaneNormalVector_double; FillVector3D(slicingPlaneNormalVector_double, slicingPlaneNormalVector[0], slicingPlaneNormalVector[1], slicingPlaneNormalVector[2]); itk::Vector slicingPlaneRightVector_double = itk::CrossProduct(slicingPlaneUpVector_double, slicingPlaneNormalVector_double); mitk::Vector3D slicingPlaneRightVector; mitk::FillVector3D(slicingPlaneRightVector, slicingPlaneRightVector_double[0], slicingPlaneRightVector_double[1], slicingPlaneRightVector_double[2]); mitk::FillVector3D(slicingPlaneUpVector, slicingPlaneUpVector_double[0], slicingPlaneUpVector_double[1], slicingPlaneUpVector_double[2]); snc->ReorientSlices(origin, slicingPlaneRightVector, slicingPlaneUpVector); snc->SelectSliceByPoint(slicePosition); } } else { MITK_ERROR << "Unsupported ViewDirection: " << m_ViewDirection; } m_Renderer->RequestUpdate(); } } diff --git a/Modules/IGT/Testing/mitkTrackingDeviceTest.cpp b/Modules/IGT/Testing/mitkTrackingDeviceTest.cpp index 0a12699607..08ba485949 100644 --- a/Modules/IGT/Testing/mitkTrackingDeviceTest.cpp +++ b/Modules/IGT/Testing/mitkTrackingDeviceTest.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. ============================================================================*/ #include "mitkTrackingDevice.h" #include "mitkTestingMacros.h" #include "mitkTrackingTool.h" #include "mitkTrackingTypes.h" #include "mitkCommon.h" #include #include #include #include #include #include #include #include "mitkTrackingDeviceTypeCollection.h" #include "mitkUnspecifiedTrackingTypeInformation.h" //All Tracking devices, which should be avaiable by default #include "mitkNDIAuroraTypeInformation.h" #include "mitkNDIPolarisTypeInformation.h" #include "mitkVirtualTrackerTypeInformation.h" #include "mitkMicronTrackerTypeInformation.h" #include "mitkNPOptitrackTrackingTypeInformation.h" #include "mitkOpenIGTLinkTypeInformation.h" /** * Create new class and derive it from TrackingDevice */ class TrackingDeviceTestClass : public mitk::TrackingDevice { public: mitkClassMacro(TrackingDeviceTestClass, mitk::TrackingDevice); itkFactorylessNewMacro(Self) itkCloneMacro(Self) bool OpenConnection() override{return true;}; bool CloseConnection() override{return true;}; - bool StartTracking() override{this->SetState(Tracking); this->m_TrackingFinishedMutex->Unlock(); return true;}; + bool StartTracking() override{this->SetState(Tracking); return true;}; mitk::TrackingTool* GetTool(unsigned int /*toolNumber*/) const override {return nullptr;}; unsigned int GetToolCount() const override {return 1;}; }; /** * This function is testing the Class TrackingDevice. For most tests we would need the MicronTracker hardware, so only a few * simple tests, which can run without the hardware are implemented yet (2009, January, 23rd). As soon as there is a working * concept to test the tracking classes which are very close to the hardware on all systems more tests are needed here. */ int mitkTrackingDeviceTest(int /* argc */, char* /*argv*/[]) { MITK_TEST_BEGIN("TrackingDevice"); mitk::TrackingDeviceTypeCollection deviceTypeCollection; deviceTypeCollection.RegisterTrackingDeviceType(new mitk::NDIAuroraTypeInformation()); deviceTypeCollection.RegisterAsMicroservice(); deviceTypeCollection.RegisterTrackingDeviceType(new mitk::VirtualTrackerTypeInformation()); deviceTypeCollection.RegisterTrackingDeviceType(new mitk::NDIPolarisTypeInformation()); deviceTypeCollection.RegisterTrackingDeviceType(new mitk::MicronTrackerTypeInformation()); // Test instantiation of TrackingDevice TrackingDeviceTestClass::Pointer trackingDeviceTestClass = TrackingDeviceTestClass::New(); MITK_TEST_CONDITION(trackingDeviceTestClass.IsNotNull(),"Test instatiation"); // Test method GetState() MITK_TEST_CONDITION(trackingDeviceTestClass->GetState()==mitk::TrackingDevice::Setup,"Mode should be initialized to SETUP"); // Test method SetType() MITK_TEST_CONDITION(trackingDeviceTestClass->GetType()==mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName(),"Type should be initialized to 'not specified'"); trackingDeviceTestClass->SetType(mitk::NDIAuroraTypeInformation::GetTrackingDeviceName()); MITK_TEST_CONDITION(trackingDeviceTestClass->GetType() == mitk::NDIAuroraTypeInformation::GetTrackingDeviceName(), "Type should be NDIAurora, as it has just been set"); trackingDeviceTestClass->SetType(mitk::NDIPolarisTypeInformation::GetTrackingDeviceName()); MITK_TEST_CONDITION(trackingDeviceTestClass->GetType() == mitk::NDIPolarisTypeInformation::GetTrackingDeviceName(), "Type should be NDIPolaris, as it has just been set"); trackingDeviceTestClass->SetType(mitk::MicronTrackerTypeInformation::GetTrackingDeviceName()); MITK_TEST_CONDITION(trackingDeviceTestClass->GetType() == mitk::MicronTrackerTypeInformation::GetTrackingDeviceName(), "Type should be ClaronMicron, as it has just been set"); trackingDeviceTestClass->SetType(mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName()); MITK_TEST_CONDITION(trackingDeviceTestClass->GetType() == mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName(), "Type should be VirtualTracker, as it has just been set"); // Test method StopTracking() trackingDeviceTestClass->StartTracking(); trackingDeviceTestClass->StopTracking(); MITK_TEST_CONDITION(trackingDeviceTestClass->GetState()== mitk::TrackingDevice::Ready,"Type should be NDIAurora, as it has just been set"); MITK_TEST_CONDITION(deviceTypeCollection.GetTrackingDeviceTypeInformation(mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName())->GetTrackingDeviceName() == mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName(), "Test GetTrackingDeviceTypeInformation"); MITK_TEST_CONDITION(deviceTypeCollection.GetTrackingDeviceTypeInformation(mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName())->m_TrackingDeviceData[0].Model == "Virtual Tracker", "Test GetTrackingDeviceTypeInformation"); std::vector names = deviceTypeCollection.GetTrackingDeviceTypeNames(); MITK_TEST_CONDITION(names[0] == mitk::NDIAuroraTypeInformation::GetTrackingDeviceName(), "Test collection name list"); MITK_TEST_CONDITION(names[1] == mitk::VirtualTrackerTypeInformation::GetTrackingDeviceName(), "Test collection name list"); MITK_TEST_CONDITION(names[2] == mitk::NDIPolarisTypeInformation::GetTrackingDeviceName(), "Test collection name list"); MITK_TEST_CONDITION(names[3] == mitk::MicronTrackerTypeInformation::GetTrackingDeviceName(), "Test collection name list"); MITK_TEST_END(); } diff --git a/Modules/IGT/TrackingDevices/mitkClaronTool.h b/Modules/IGT/TrackingDevices/mitkClaronTool.h index 3575653442..e642253c01 100644 --- a/Modules/IGT/TrackingDevices/mitkClaronTool.h +++ b/Modules/IGT/TrackingDevices/mitkClaronTool.h @@ -1,82 +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. ============================================================================*/ #ifndef MITKCLARONTOOL_H_HEADER_INCLUDED_ #define MITKCLARONTOOL_H_HEADER_INCLUDED_ #include #include -#include namespace mitk { class ClaronTrackingDevice; /** Documentation: * \brief An object of this class represents a MicronTracker 2 tool. * A tool has to be added to a tracking device which will then * continuously update the tool coordinates. * \ingroup IGT */ class MITKIGT_EXPORT ClaronTool : public TrackingTool { public: friend class ClaronTrackingDevice; mitkClassMacro(ClaronTool, TrackingTool); /** * \brief Loads a tool calibration file. Without this file the tool can not be tracked! */ bool LoadFile(const char* filename); /** * \brief Loads a tool calibration file. Without this file the tool can not be tracked! */ bool LoadFile(std::string filename); std::string GetFile(); /** * \brief Sets the handle of the tool. * \param handle The new handle of the tool. */ void SetToolHandle (claronToolHandle handle); /** * \return Returns the calibration name which is used to identify the tool. */ std::string GetCalibrationName(); /** * \brief Sets the calibration name of the tool. Be careful, only use this method if you know what you are doing. * If you want to change the tool name use the method setToolName instead! */ void SetCalibrationName(std::string name); /** * @return Returns the tool handle of the tool. */ claronToolHandle GetToolHandle(); protected: itkFactorylessNewMacro(Self); itkCloneMacro(Self) ClaronTool(); ~ClaronTool() override; /** \brief Tool handle variable from tracking device */ claronToolHandle m_ToolHandle; /** \brief Variable which holds the Tool's calibration name */ std::string m_CalibrationName; /** \brief Variable to check filename's format and to get back complete filename */ std::string m_Filename; }; }//mitk #endif // MITKCLARONTOOL_H_HEADER_INCLUDED_ diff --git a/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.cpp index db2ecf050c..9be11acfcb 100644 --- a/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.cpp +++ b/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.cpp @@ -1,327 +1,306 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkClaronTrackingDevice.h" #include "mitkClaronTool.h" #include "mitkIGTConfig.h" #include "mitkIGTTimeStamp.h" #include "mitkIGTHardwareException.h" #include #include -#include #include #include -typedef itk::MutexLockHolder MutexLockHolder; - mitk::ClaronTrackingDevice::ClaronTrackingDevice(): mitk::TrackingDevice() { //set the type of this tracking device this->m_Data = mitk::MicronTrackerTypeInformation::GetDeviceDataMicronTrackerH40(); - this->m_MultiThreader = itk::MultiThreader::New(); - m_ThreadID = 0; - m_Device = mitk::ClaronInterface::New(); //############################# standard directories ################################## if (m_Device->IsMicronTrackerInstalled()) { m_ToolfilesDir = mitk::IOUtil::CreateTemporaryDirectory(); #ifdef MITK_MICRON_TRACKER_CALIBRATION_DIR m_CalibrationDir = std::string(MITK_MICRON_TRACKER_CALIBRATION_DIR); #endif } else { m_ToolfilesDir = "Error - No Microntracker installed"; m_CalibrationDir = "Error - No Microntracker installed"; } //################################################################################################## m_Device->Initialize(m_CalibrationDir, m_ToolfilesDir); } bool mitk::ClaronTrackingDevice::IsDeviceInstalled() { mitk::ClaronInterface::Pointer tempInterface = mitk::ClaronInterface::New(); return tempInterface->IsMicronTrackerInstalled(); } mitk::ClaronTrackingDevice::~ClaronTrackingDevice() { } mitk::TrackingTool* mitk::ClaronTrackingDevice::AddTool( const char* toolName, const char* fileName ) { mitk::ClaronTool::Pointer t = mitk::ClaronTool::New(); if (t->LoadFile(fileName) == false) { return nullptr; } t->SetToolName(toolName); if (this->InternalAddTool(t) == false) return nullptr; return t.GetPointer(); } bool mitk::ClaronTrackingDevice::InternalAddTool(ClaronTool::Pointer tool) { m_AllTools.push_back(tool); return true; } std::vector mitk::ClaronTrackingDevice::DetectTools() { std::vector returnValue; std::vector allHandles = m_Device->GetAllActiveTools(); for (auto iter = allHandles.begin(); iter != allHandles.end(); ++iter) { ClaronTool::Pointer newTool = ClaronTool::New(); newTool->SetToolName(m_Device->GetName(*iter)); newTool->SetCalibrationName(m_Device->GetName(*iter)); newTool->SetToolHandle(*iter); returnValue.push_back(newTool); } return returnValue; } bool mitk::ClaronTrackingDevice::StartTracking() { //By Alfred: next line because no temp directory is set if MicronTracker is not installed if (!m_Device->IsMicronTrackerInstalled()) return false; //################################################################################## //be sure that the temp-directory is empty at start: delete all files in the tool files directory itksys::SystemTools::RemoveADirectory(m_ToolfilesDir.c_str()); itksys::SystemTools::MakeDirectory(m_ToolfilesDir.c_str()); //copy all toolfiles into the temp directory for (unsigned int i=0; iGetFile().c_str(), m_ToolfilesDir.c_str()); } this->SetState(Tracking); // go to mode Tracking - this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking + this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking this->m_StopTracking = false; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); //restart the Microntracker, so it will load the new tool files m_Device->StopTracking(); m_Device->Initialize(m_CalibrationDir,m_ToolfilesDir); if (m_Device->StartTracking()) { mitk::IGTTimeStamp::GetInstance()->Start(this); - m_ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); // start a new thread that executes the TrackTools() method + m_Thread = std::thread(&ClaronTrackingDevice::ThreadStartTracking, this); // start a new thread that executes the TrackTools() method return true; } else {mitkThrowException(mitk::IGTHardwareException) << "Error while trying to start the device!";} } bool mitk::ClaronTrackingDevice::StopTracking() { Superclass::StopTracking(); //delete all files in the tool files directory itksys::SystemTools::RemoveADirectory(m_ToolfilesDir.c_str()); return true; } unsigned int mitk::ClaronTrackingDevice::GetToolCount() const { return (unsigned int)this->m_AllTools.size(); } mitk::TrackingTool* mitk::ClaronTrackingDevice::GetTool(unsigned int toolNumber) const { if ( toolNumber >= this->GetToolCount()) return nullptr; else return this->m_AllTools[toolNumber]; } bool mitk::ClaronTrackingDevice::OpenConnection() { bool returnValue; //Create the temp directory itksys::SystemTools::MakeDirectory(m_ToolfilesDir.c_str()); m_Device->Initialize(m_CalibrationDir,m_ToolfilesDir); returnValue = m_Device->StartTracking(); if (returnValue) { this->SetState(Ready); } else { //reset everything if (m_Device.IsNull()) { m_Device = mitk::ClaronInterface::New(); m_Device->Initialize(m_CalibrationDir, m_ToolfilesDir); } m_Device->StopTracking(); this->SetState(Setup); mitkThrowException(mitk::IGTHardwareException) << "Error while trying to open connection to the MicronTracker."; } return returnValue; } bool mitk::ClaronTrackingDevice::CloseConnection() { bool returnValue = true; if (this->GetState() == Setup) return true; returnValue = m_Device->StopTracking(); //delete the temporary directory itksys::SystemTools::RemoveADirectory(m_ToolfilesDir.c_str()); this->SetState(Setup); return returnValue; } mitk::ClaronInterface* mitk::ClaronTrackingDevice::GetDevice() { return m_Device; } std::vector mitk::ClaronTrackingDevice::GetAllTools() { return this->m_AllTools; } void mitk::ClaronTrackingDevice::TrackTools() { try { /* lock the TrackingFinishedMutex to signal that the execution rights are now transfered to the tracking thread */ - MutexLockHolder trackingFinishedLockHolder(*m_TrackingFinishedMutex); // keep lock until end of scope + std::lock_guard trackingFinishedLockHolder(m_TrackingFinishedMutex); // keep lock until end of scope bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here - this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking + this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking localStopTracking = this->m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); while ((this->GetState() == Tracking) && (localStopTracking == false)) { this->GetDevice()->GrabFrame(); std::vector detectedTools = this->DetectTools(); std::vector allTools = this->GetAllTools(); std::vector::iterator itAllTools; for(itAllTools = allTools.begin(); itAllTools != allTools.end(); itAllTools++) { mitk::ClaronTool::Pointer currentTool = *itAllTools; //test if current tool was detected std::vector::iterator itDetectedTools; bool foundTool = false; for(itDetectedTools = detectedTools.begin(); itDetectedTools != detectedTools.end(); itDetectedTools++) { mitk::ClaronTool::Pointer aktuDet = *itDetectedTools; std::string tempString(currentTool->GetCalibrationName()); if (tempString.compare(aktuDet->GetCalibrationName())==0) { currentTool->SetToolHandle(aktuDet->GetToolHandle()); foundTool = true; } } if (!foundTool) { currentTool->SetToolHandle(0); } if (currentTool->GetToolHandle() != 0) { currentTool->SetDataValid(true); //get tip position of tool: std::vector pos_vector = this->GetDevice()->GetTipPosition(currentTool->GetToolHandle()); //write tip position into tool: mitk::Point3D pos; pos[0] = pos_vector[0]; pos[1] = pos_vector[1]; pos[2] = pos_vector[2]; currentTool->SetPosition(pos); //get tip quaternion of tool std::vector quat = this->GetDevice()->GetTipQuaternions(currentTool->GetToolHandle()); //write tip quaternion into tool mitk::Quaternion orientation(quat[1], quat[2], quat[3], quat[0]); currentTool->SetOrientation(orientation); //TODO: read the timestamp data from the tracking device interface currentTool->SetIGTTimeStamp(mitk::IGTTimeStamp::GetInstance()->GetElapsed()); } else { mitk::Point3D origin; origin.Fill(0); currentTool->SetPosition(origin); currentTool->SetOrientation(mitk::Quaternion(0,0,0,0)); currentTool->SetDataValid(false); } } /* Update the local copy of m_StopTracking */ - this->m_StopTrackingMutex->Lock(); + this->m_StopTrackingMutex.lock(); localStopTracking = m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); } } catch(...) { this->StopTracking(); mitkThrowException(mitk::IGTHardwareException) << "Error while trying to track tools. Thread stopped."; } } bool mitk::ClaronTrackingDevice::IsMicronTrackerInstalled() { return this->m_Device->IsMicronTrackerInstalled(); } -ITK_THREAD_RETURN_TYPE mitk::ClaronTrackingDevice::ThreadStartTracking(void* pInfoStruct) +void mitk::ClaronTrackingDevice::ThreadStartTracking() { - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - if (pInfo == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - ClaronTrackingDevice *trackingDevice = (ClaronTrackingDevice*)pInfo->UserData; - - if (trackingDevice != nullptr) - trackingDevice->TrackTools(); - - return ITK_THREAD_RETURN_VALUE; + this->TrackTools(); } diff --git a/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.h b/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.h index 64c8591528..1ab4f4daf1 100644 --- a/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.h +++ b/Modules/IGT/TrackingDevices/mitkClaronTrackingDevice.h @@ -1,165 +1,164 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKCLARONTRACKINGDEVICE_H_HEADER_INCLUDED_ #define MITKCLARONTRACKINGDEVICE_H_HEADER_INCLUDED_ +#include #include #include #include #include -#include //only include MicronTracker if cmake Variable is on else the ClaronInterfaceStub is included #ifdef MITK_USE_MICRON_TRACKER #include #else #include #endif namespace mitk { /** Documentation: * \brief An object of this class represents the MicronTracker device. You can add tools to this * device, then open the connection and start tracking. The tracking device will then * continuously update the tool coordinates. * \ingroup IGT */ class MITKIGT_EXPORT ClaronTrackingDevice : public TrackingDevice { public: mitkClassMacro(ClaronTrackingDevice, TrackingDevice); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * @returns Returns true if the MicronTracker is installed on this build (means activated in CMAKE). False if not. */ bool IsDeviceInstalled() override; /** * \brief Starts the tracking. * \return Returns true if the tracking is started. Throws an exception if an error occures. * @throw mitk::IGTHardwareException Throws an exception if there is an error during start tracking. */ bool StartTracking() override; /** * \brief Stops the tracking. * \return Returns true if the tracking is stopped. */ bool StopTracking() override; /** * \brief Opens the connection to the device. This have to be done before the tracking is started. * @throw mitk::IGTHardwareException Throws an exception if there is an error during open connection. */ bool OpenConnection() override; /** * \brief Closes the connection and clears all resources. */ bool CloseConnection() override; /** * \return Returns the number of tools which have been added to the device. */ unsigned int GetToolCount() const override; /** * \param toolNumber The number of the tool which should be given back. * \return Returns the tool which the number "toolNumber". Returns nullptr, if there is * no tool with this number. */ TrackingTool* GetTool(unsigned int toolNumber) const override; /** * \brief Create a new Claron tool with toolName and fileName and add it to the list of tools * * This method will create a new ClaronTool object, load the tool definition file fileName, * set the tool name toolName and then add it to the list of tools. * It returns a pointer of type mitk::TrackingTool to the tool * that can be used to read tracking data from it. * This is the only way to add tools to ClaronTrackingDevice. * * \warning adding tools is not possible in tracking mode, only in setup and ready. */ mitk::TrackingTool* AddTool(const char* toolName, const char* fileName); /** * \return Returns whether the MicronTracker is installed (means whether the C-Make-Variable "MITK_USE_MICRON_TRACKER" is set), * so returns false in this case. * \deprecatedSince{2014_03} This method is deprecated, please use the static method IsDeviceInstalled() instead. */ DEPRECATED(bool IsMicronTrackerInstalled()); /** @brief Sets the directory where the calibration file of the MicronTracker can be found. */ itkSetMacro(CalibrationDir,std::string); /** @brief Gets the current calibration directory. */ itkGetMacro(CalibrationDir,std::string); protected: ClaronTrackingDevice(); ~ClaronTrackingDevice() override; /** * \brief Adds a tool to the tracking device. * * \param tool The tool which will be added. * \return Returns true if the tool has been added, false otherwise. */ bool InternalAddTool(ClaronTool::Pointer tool); /** * \brief This method tracks tools as long as the variable m_Mode is set to "Tracking". * Tracking tools means grabbing frames from the camera an updating the tools. * @throw mitk::IGTHardwareException Throws an exception if there is an error during tracking of tools. */ void TrackTools(); /** * \brief Automatically detects tools in field of measurement of the tracking device. * Tools can only be detected if their calibration file is availiable in the directory * for calibration files. * \return Returns all detected Tools. */ std::vector DetectTools(); /** * \return Returns all tools of the tracking device. */ std::vector GetAllTools(); /** * \return Gives back the device which is represented by an object of the class ClaronInterface. */ ClaronInterface* GetDevice(); - static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* data); + void ThreadStartTracking(); std::vector m_AllTools; ///< vector holding all tools ClaronInterface::Pointer m_Device; ///< represents the interface to the tracking hardware - itk::MultiThreader::Pointer m_MultiThreader; - int m_ThreadID; + std::thread m_Thread; /** \brief The directory where the camera calibration files can be found */ std::string m_CalibrationDir; /** \brief The directory where the tool calibration files can be found */ std::string m_ToolfilesDir; }; }//mitk #endif /* MITKCLARONTRACKINGDEVICE_H_HEADER_INCLUDED_ */ diff --git a/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.cpp index fac6bcda42..659914e77b 100644 --- a/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.cpp +++ b/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.cpp @@ -1,1349 +1,1324 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkNDITrackingDevice.h" #include "mitkIGTTimeStamp.h" #include "mitkIGTHardwareException.h" #include #include -#include #include #include #include // vtk #include -typedef itk::MutexLockHolder MutexLockHolder; - const unsigned char CR = 0xD; // == '\r' - carriage return const unsigned char LF = 0xA; // == '\n' - line feed mitk::NDITrackingDevice::NDITrackingDevice() : TrackingDevice(), m_DeviceName(""), m_PortNumber(mitk::SerialCommunication::COM5), m_BaudRate(mitk::SerialCommunication::BaudRate9600), m_DataBits(mitk::SerialCommunication::DataBits8), m_Parity(mitk::SerialCommunication::None), m_StopBits(mitk::SerialCommunication::StopBits1), m_HardwareHandshake(mitk::SerialCommunication::HardwareHandshakeOff), -m_IlluminationActivationRate(Hz20), m_DataTransferMode(TX), m_6DTools(), m_ToolsMutex(nullptr), -m_SerialCommunication(nullptr), m_SerialCommunicationMutex(nullptr), m_DeviceProtocol(nullptr), -m_MultiThreader(nullptr), m_ThreadID(0), m_OperationMode(ToolTracking6D), m_MarkerPointsMutex(nullptr), m_MarkerPoints() +m_IlluminationActivationRate(Hz20), m_DataTransferMode(TX), m_6DTools(), +m_SerialCommunication(nullptr), m_DeviceProtocol(nullptr), +m_OperationMode(ToolTracking6D), m_MarkerPoints() { m_Data = mitk::UnspecifiedTrackingTypeInformation::GetDeviceDataUnspecified(); m_6DTools.clear(); - m_SerialCommunicationMutex = itk::FastMutexLock::New(); m_DeviceProtocol = NDIProtocol::New(); m_DeviceProtocol->SetTrackingDevice(this); m_DeviceProtocol->UseCRCOn(); - m_MultiThreader = itk::MultiThreader::New(); - m_ToolsMutex = itk::FastMutexLock::New(); - m_MarkerPointsMutex = itk::FastMutexLock::New(); m_MarkerPoints.reserve(50); // a maximum of 50 marker positions can be reported by the tracking device } bool mitk::NDITrackingDevice::UpdateTool(mitk::TrackingTool* tool) { if (this->GetState() != Setup) { mitk::NDIPassiveTool* ndiTool = dynamic_cast(tool); if (ndiTool == nullptr) return false; std::string portHandle = ndiTool->GetPortHandle(); //return false if the SROM Data has not been set if (ndiTool->GetSROMData() == nullptr) return false; NDIErrorCode returnvalue; returnvalue = m_DeviceProtocol->PVWR(&portHandle, ndiTool->GetSROMData(), ndiTool->GetSROMDataLength()); if (returnvalue != NDIOKAY) return false; returnvalue = m_DeviceProtocol->PINIT(&portHandle); if (returnvalue != NDIOKAY) return false; returnvalue = m_DeviceProtocol->PENA(&portHandle, ndiTool->GetTrackingPriority()); // Enable tool if (returnvalue != NDIOKAY) return false; return true; } else { return false; } } void mitk::NDITrackingDevice::SetRotationMode(RotationMode r) { m_RotationMode = r; } mitk::NDITrackingDevice::~NDITrackingDevice() { /* stop tracking and disconnect from tracking device */ if (GetState() == Tracking) { this->StopTracking(); } if (GetState() == Ready) { this->CloseConnection(); } /* cleanup tracking thread */ - if ((m_ThreadID != 0) && (m_MultiThreader.IsNotNull())) - { - m_MultiThreader->TerminateThread(m_ThreadID); - } - m_MultiThreader = nullptr; + if (m_Thread.joinable()) + m_Thread.join(); + /* free serial communication interface */ if (m_SerialCommunication.IsNotNull()) { m_SerialCommunication->ClearReceiveBuffer(); m_SerialCommunication->ClearSendBuffer(); m_SerialCommunication->CloseConnection(); m_SerialCommunication = nullptr; } } void mitk::NDITrackingDevice::SetPortNumber(const PortNumber _arg) { if (this->GetState() != Setup) return; itkDebugMacro("setting PortNumber to " << _arg); if (this->m_PortNumber != _arg) { this->m_PortNumber = _arg; this->Modified(); } } void mitk::NDITrackingDevice::SetDeviceName(std::string _arg) { if (this->GetState() != Setup) return; itkDebugMacro("setting eviceName to " << _arg); if (this->m_DeviceName != _arg) { this->m_DeviceName = _arg; this->Modified(); } } void mitk::NDITrackingDevice::SetBaudRate(const BaudRate _arg) { if (this->GetState() != Setup) return; itkDebugMacro("setting BaudRate to " << _arg); if (this->m_BaudRate != _arg) { this->m_BaudRate = _arg; this->Modified(); } } void mitk::NDITrackingDevice::SetDataBits(const DataBits _arg) { if (this->GetState() != Setup) return; itkDebugMacro("setting DataBits to " << _arg); if (this->m_DataBits != _arg) { this->m_DataBits = _arg; this->Modified(); } } void mitk::NDITrackingDevice::SetParity(const Parity _arg) { if (this->GetState() != Setup) return; itkDebugMacro("setting Parity to " << _arg); if (this->m_Parity != _arg) { this->m_Parity = _arg; this->Modified(); } } void mitk::NDITrackingDevice::SetStopBits(const StopBits _arg) { if (this->GetState() != Setup) return; itkDebugMacro("setting StopBits to " << _arg); if (this->m_StopBits != _arg) { this->m_StopBits = _arg; this->Modified(); } } void mitk::NDITrackingDevice::SetHardwareHandshake(const HardwareHandshake _arg) { if (this->GetState() != Setup) return; itkDebugMacro("setting HardwareHandshake to " << _arg); if (this->m_HardwareHandshake != _arg) { this->m_HardwareHandshake = _arg; this->Modified(); } } void mitk::NDITrackingDevice::SetIlluminationActivationRate(const IlluminationActivationRate _arg) { if (this->GetState() == Tracking) return; itkDebugMacro("setting IlluminationActivationRate to " << _arg); if (this->m_IlluminationActivationRate != _arg) { this->m_IlluminationActivationRate = _arg; this->Modified(); if (this->GetState() == Ready) // if the connection to the tracking system is established, send the new rate to the tracking device too m_DeviceProtocol->IRATE(this->m_IlluminationActivationRate); } } void mitk::NDITrackingDevice::SetDataTransferMode(const DataTransferMode _arg) { itkDebugMacro("setting DataTransferMode to " << _arg); if (this->m_DataTransferMode != _arg) { this->m_DataTransferMode = _arg; this->Modified(); } } mitk::NDIErrorCode mitk::NDITrackingDevice::Send(const std::string* input, bool addCRC) { if (input == nullptr) return SERIALSENDERROR; std::string message; if (addCRC == true) message = *input + CalcCRC(input) + std::string(1, CR); else message = *input + std::string(1, CR); //unsigned int messageLength = message.length() + 1; // +1 for CR // Clear send buffer this->ClearSendBuffer(); // Send the date to the device - MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex + std::lock_guard lock(m_SerialCommunicationMutex); long returnvalue = m_SerialCommunication->Send(message); if (returnvalue == 0) return SERIALSENDERROR; else return NDIOKAY; } mitk::NDIErrorCode mitk::NDITrackingDevice::Receive(std::string* answer, unsigned int numberOfBytes) { if (answer == nullptr) return SERIALRECEIVEERROR; - MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex + std::lock_guard lock(m_SerialCommunicationMutex); long returnvalue = m_SerialCommunication->Receive(*answer, numberOfBytes); // never read more bytes than the device has send, the function will block until enough bytes are send... if (returnvalue == 0) return SERIALRECEIVEERROR; else return NDIOKAY; } mitk::NDIErrorCode mitk::NDITrackingDevice::ReceiveByte(char* answer) { if (answer == nullptr) return SERIALRECEIVEERROR; std::string m; - MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex + std::lock_guard lock(m_SerialCommunicationMutex); long returnvalue = m_SerialCommunication->Receive(m, 1); if ((returnvalue == 0) || (m.size() != 1)) return SERIALRECEIVEERROR; *answer = m.at(0); return NDIOKAY; } mitk::NDIErrorCode mitk::NDITrackingDevice::ReceiveLine(std::string* answer) { if (answer == nullptr) return SERIALRECEIVEERROR; std::string m; - MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex + std::lock_guard lock(m_SerialCommunicationMutex); do { long returnvalue = m_SerialCommunication->Receive(m, 1); if ((returnvalue == 0) || (m.size() != 1)) return SERIALRECEIVEERROR; *answer += m; } while (m.at(0) != LF); return NDIOKAY; } void mitk::NDITrackingDevice::ClearSendBuffer() { - MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex + std::lock_guard lock(m_SerialCommunicationMutex); m_SerialCommunication->ClearSendBuffer(); } void mitk::NDITrackingDevice::ClearReceiveBuffer() { - MutexLockHolder lock(*m_SerialCommunicationMutex); // lock and unlock the mutex + std::lock_guard lock(m_SerialCommunicationMutex); m_SerialCommunication->ClearReceiveBuffer(); } const std::string mitk::NDITrackingDevice::CalcCRC(const std::string* input) { if (input == nullptr) return ""; /* the crc16 calculation code is taken from the NDI API guide example code section */ static int oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; unsigned int data; // copy of the input string's current character unsigned int crcValue = 0; // the crc value is stored here unsigned int* puCRC16 = &crcValue; // the algorithm uses a pointer to crcValue, so it's easier to provide that than to change the algorithm for (unsigned int i = 0; i < input->length(); i++) { data = (*input)[i]; data = (data ^ (*(puCRC16)& 0xff)) & 0xff; *puCRC16 >>= 8; if (oddparity[data & 0x0f] ^ oddparity[data >> 4]) { *(puCRC16) ^= 0xc001; } data <<= 6; *puCRC16 ^= data; data <<= 1; *puCRC16 ^= data; } // crcValue contains now the CRC16 value. Convert it to a string and return it char returnvalue[13]; sprintf(returnvalue, "%04X", crcValue); // 4 hexadecimal digit with uppercase format return std::string(returnvalue); } bool mitk::NDITrackingDevice::OpenConnection() { - //this->m_ModeMutex->Lock(); + //this->m_ModeMutex.lock(); if (this->GetState() != Setup) { mitkThrowException(mitk::IGTException) << "Can only try to open the connection if in setup mode"; } m_SerialCommunication = mitk::SerialCommunication::New(); /* init local com port to standard com settings for a NDI tracking device: 9600 baud, 8 data bits, no parity, 1 stop bit, no hardware handshake */ if (m_DeviceName.empty()) m_SerialCommunication->SetPortNumber(m_PortNumber); else m_SerialCommunication->SetDeviceName(m_DeviceName); m_SerialCommunication->SetBaudRate(mitk::SerialCommunication::BaudRate9600); m_SerialCommunication->SetDataBits(mitk::SerialCommunication::DataBits8); m_SerialCommunication->SetParity(mitk::SerialCommunication::None); m_SerialCommunication->SetStopBits(mitk::SerialCommunication::StopBits1); m_SerialCommunication->SetSendTimeout(5000); m_SerialCommunication->SetReceiveTimeout(5000); if (m_SerialCommunication->OpenConnection() == 0) // 0 == ERROR_VALUE { m_SerialCommunication->CloseConnection(); m_SerialCommunication = nullptr; mitkThrowException(mitk::IGTHardwareException) << "Can not open serial port"; } /* Reset Tracking device by sending a serial break for 500ms */ m_SerialCommunication->SendBreak(400); /* Read answer from tracking device (RESETBE6F) */ static const std::string reset("RESETBE6F\r"); std::string answer = ""; this->Receive(&answer, reset.length()); // read answer (should be RESETBE6F) this->ClearReceiveBuffer(); // flush the receive buffer of all remaining data (carriage return, strings other than reset if (reset.compare(answer) != 0) // check for RESETBE6F { if (m_SerialCommunication.IsNotNull()) { m_SerialCommunication->CloseConnection(); m_SerialCommunication = nullptr; } mitkThrowException(mitk::IGTHardwareException) << "Hardware Reset of tracking device did not work"; } /* Now the tracking device isSetData reset, start initialization */ NDIErrorCode returnvalue; /* set device com settings to new values and wait for the device to change them */ returnvalue = m_DeviceProtocol->COMM(m_BaudRate, m_DataBits, m_Parity, m_StopBits, m_HardwareHandshake); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << "Could not set comm settings in trackingdevice"; } //after changing COMM wait at least 100ms according to NDI Api documentation page 31 itksys::SystemTools::Delay(500); /* now change local com settings accordingly */ m_SerialCommunication->CloseConnection(); m_SerialCommunication->SetBaudRate(m_BaudRate); m_SerialCommunication->SetDataBits(m_DataBits); m_SerialCommunication->SetParity(m_Parity); m_SerialCommunication->SetStopBits(m_StopBits); m_SerialCommunication->SetHardwareHandshake(m_HardwareHandshake); m_SerialCommunication->SetSendTimeout(5000); m_SerialCommunication->SetReceiveTimeout(5000); m_SerialCommunication->OpenConnection(); /* initialize the tracking device */ returnvalue = m_DeviceProtocol->INIT(); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << "Could not initialize the tracking device"; } if (this->GetType() == mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName()) // if the type of tracking device is not specified, try to query the connected device { mitk::TrackingDeviceType deviceType; returnvalue = m_DeviceProtocol->VER(deviceType); if ((returnvalue != NDIOKAY) || (deviceType == mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName())) { mitkThrowException(mitk::IGTHardwareException) << "Could not determine tracking device type. Please set manually and try again."; } this->SetType(deviceType); } /**** Optional Polaris specific code, Work in progress // start diagnostic mode returnvalue = m_DeviceProtocol->DSTART(); if (returnvalue != NDIOKAY) { this->SetErrorMessage("Could not start diagnostic mode"); return false; } else // we are in diagnostic mode { // initialize extensive IR checking returnvalue = m_DeviceProtocol->IRINIT(); if (returnvalue != NDIOKAY) { this->SetErrorMessage("Could not initialize intense infrared light checking"); return false; } bool intenseIR = false; returnvalue = m_DeviceProtocol->IRCHK(&intenseIR); if (returnvalue != NDIOKAY) { this->SetErrorMessage("Could not execute intense infrared light checking"); return false; } if (intenseIR == true) // do something - warn the user, raise exception, write to protocol or similar std::cout << "Warning: Intense infrared light detected. Accurate tracking will probably not be possible.\n"; // stop diagnictic mode returnvalue = m_DeviceProtocol->DSTOP(); if (returnvalue != NDIOKAY) { this->SetErrorMessage("Could not stop diagnostic mode"); return false; } } *** end of optional polaris code ***/ /** * now add tools to the tracking system **/ /* First, check if the tracking device has port handles that need to be freed and free them */ returnvalue = FreePortHandles(); // non-critical, therefore no error handling /** * POLARIS: initialize the tools that were added manually **/ { - MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard lock(m_ToolsMutex); std::string portHandle; auto endIt = m_6DTools.end(); for (auto it = m_6DTools.begin(); it != endIt; ++it) { /* get a port handle for the tool */ returnvalue = m_DeviceProtocol->PHRQ(&portHandle); if (returnvalue == NDIOKAY) { (*it)->SetPortHandle(portHandle.c_str()); /* now write the SROM file of the tool to the tracking system using PVWR */ if (this->m_Data.Line == mitk::NDIPolarisTypeInformation::GetTrackingDeviceName()) { returnvalue = m_DeviceProtocol->PVWR(&portHandle, (*it)->GetSROMData(), (*it)->GetSROMDataLength()); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not write SROM file for tool '") + (*it)->GetToolName() + std::string("' to tracking device")).c_str(); } returnvalue = m_DeviceProtocol->PINIT(&portHandle); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not initialize tool '") + (*it)->GetToolName()).c_str(); } if ((*it)->IsEnabled() == true) { returnvalue = m_DeviceProtocol->PENA(&portHandle, (*it)->GetTrackingPriority()); // Enable tool if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not enable port '") + portHandle + std::string("' for tool '") + (*it)->GetToolName() + std::string("'")).c_str(); } } } } } } // end of toolsmutexlockholder scope /* check for wired tools and add them too */ if (this->DiscoverWiredTools() == false) // query the tracking device for wired tools and add them to our tool list return false; // \TODO: could we continue anyways? /*POLARIS: set the illuminator activation rate */ if (this->m_Data.Line == mitk::NDIPolarisTypeInformation::GetTrackingDeviceName()) { returnvalue = m_DeviceProtocol->IRATE(this->m_IlluminationActivationRate); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << "Could not set the illuminator activation rate"; } } /* finish - now all tools should be added, initialized and enabled, so that tracking can be started */ this->SetState(Ready); try { SetVolume(this->m_Data); } catch (const mitk::IGTHardwareException& e) { MITK_WARN << e.GetDescription(); } return true; } bool mitk::NDITrackingDevice::InitializeWiredTools() { NDIErrorCode returnvalue; std::string portHandle; returnvalue = m_DeviceProtocol->PHSR(OCCUPIED, &portHandle); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << "Could not obtain a list of port handles that are connected"; } /* if there are port handles that need to be initialized, initialize them. Furthermore instantiate tools for each handle that has no tool yet. */ std::string ph; for (unsigned int i = 0; i < portHandle.size(); i += 2) { ph = portHandle.substr(i, 2); mitk::NDIPassiveTool* pt = this->GetInternalTool(ph); if (pt == nullptr) // if we don't have a tool, something is wrong. Tools should be discovered first by calling DiscoverWiredTools() continue; if (pt->GetSROMData() == nullptr) continue; returnvalue = m_DeviceProtocol->PVWR(&ph, pt->GetSROMData(), pt->GetSROMDataLength()); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not write SROM file for tool '") + pt->GetToolName() + std::string("' to tracking device")).c_str(); } returnvalue = m_DeviceProtocol->PINIT(&ph); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not initialize tool '") + pt->GetToolName()).c_str(); } if (pt->IsEnabled() == true) { returnvalue = m_DeviceProtocol->PENA(&ph, pt->GetTrackingPriority()); // Enable tool if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not enable port '") + portHandle + std::string("' for tool '") + pt->GetToolName() + std::string("'")).c_str(); } } } return true; } mitk::TrackingDeviceType mitk::NDITrackingDevice::TestConnection() { if (this->GetState() != Setup) { return mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName(); } m_SerialCommunication = mitk::SerialCommunication::New(); //m_DeviceProtocol = mitk::NDIProtocol::New(); //m_DeviceProtocol->SetTrackingDevice(this); //m_DeviceProtocol->UseCRCOn(); /* init local com port to standard com settings for a NDI tracking device: 9600 baud, 8 data bits, no parity, 1 stop bit, no hardware handshake */ if (m_DeviceName.empty()) m_SerialCommunication->SetPortNumber(m_PortNumber); else m_SerialCommunication->SetDeviceName(m_DeviceName); m_SerialCommunication->SetBaudRate(mitk::SerialCommunication::BaudRate9600); m_SerialCommunication->SetDataBits(mitk::SerialCommunication::DataBits8); m_SerialCommunication->SetParity(mitk::SerialCommunication::None); m_SerialCommunication->SetStopBits(mitk::SerialCommunication::StopBits1); m_SerialCommunication->SetSendTimeout(5000); m_SerialCommunication->SetReceiveTimeout(5000); if (m_SerialCommunication->OpenConnection() == 0) // error { m_SerialCommunication = nullptr; return mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName(); } /* Reset Tracking device by sending a serial break for 500ms */ m_SerialCommunication->SendBreak(400); /* Read answer from tracking device (RESETBE6F) */ static const std::string reset("RESETBE6F\r"); std::string answer = ""; this->Receive(&answer, reset.length()); // read answer (should be RESETBE6F) this->ClearReceiveBuffer(); // flush the receive buffer of all remaining data (carriage return, strings other than reset if (reset.compare(answer) != 0) // check for RESETBE6F { m_SerialCommunication->CloseConnection(); m_SerialCommunication = nullptr; mitkThrowException(mitk::IGTHardwareException) << "Hardware Reset of tracking device did not work"; } /* Now the tracking device is reset, start initialization */ NDIErrorCode returnvalue; /* initialize the tracking device */ //returnvalue = m_DeviceProtocol->INIT(); //if (returnvalue != NDIOKAY) //{ // this->SetErrorMessage("Could not initialize the tracking device"); // return mitk::TrackingSystemNotSpecified; //} mitk::TrackingDeviceType deviceType; returnvalue = m_DeviceProtocol->VER(deviceType); if ((returnvalue != NDIOKAY) || (deviceType == mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName())) { m_SerialCommunication = nullptr; return mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName(); } m_SerialCommunication = nullptr; return deviceType; } bool mitk::NDITrackingDevice::CloseConnection() { if (this->GetState() != Setup) { //init before closing to force the field generator from aurora to switch itself off m_DeviceProtocol->INIT(); /* close the serial connection */ m_SerialCommunication->CloseConnection(); /* invalidate all tools */ this->InvalidateAll(); /* return to setup mode */ this->SetState(Setup); m_SerialCommunication = nullptr; } return true; } -ITK_THREAD_RETURN_TYPE mitk::NDITrackingDevice::ThreadStartTracking(void* pInfoStruct) +void mitk::NDITrackingDevice::ThreadStartTracking() { - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - if (pInfo == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - NDITrackingDevice *trackingDevice = (NDITrackingDevice*)pInfo->UserData; - if (trackingDevice != nullptr) + if (this->GetOperationMode() == ToolTracking6D) + this->TrackTools(); // call TrackTools() from the original object + else if (this->GetOperationMode() == MarkerTracking3D) + this->TrackMarkerPositions(); // call TrackMarkerPositions() from the original object + else if (this->GetOperationMode() == ToolTracking5D) + this->TrackMarkerPositions(); // call TrackMarkerPositions() from the original object + else if (this->GetOperationMode() == HybridTracking) { - if (trackingDevice->GetOperationMode() == ToolTracking6D) - trackingDevice->TrackTools(); // call TrackTools() from the original object - else if (trackingDevice->GetOperationMode() == MarkerTracking3D) - trackingDevice->TrackMarkerPositions(); // call TrackMarkerPositions() from the original object - else if (trackingDevice->GetOperationMode() == ToolTracking5D) - trackingDevice->TrackMarkerPositions(); // call TrackMarkerPositions() from the original object - else if (trackingDevice->GetOperationMode() == HybridTracking) - { - trackingDevice->TrackToolsAndMarkers(); - } + this->TrackToolsAndMarkers(); } - trackingDevice->m_ThreadID = 0; // erase thread id, now that this thread will end. - return ITK_THREAD_RETURN_VALUE; } bool mitk::NDITrackingDevice::StartTracking() { if (this->GetState() != Ready) return false; this->SetState(Tracking); // go to mode Tracking - this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking + this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking this->m_StopTracking = false; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); - m_ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); // start a new thread that executes the TrackTools() method + m_Thread = std::thread(&NDITrackingDevice::ThreadStartTracking, this); // start a new thread that executes the TrackTools() method mitk::IGTTimeStamp::GetInstance()->Start(this); return true; } void mitk::NDITrackingDevice::TrackTools() { /* lock the TrackingFinishedMutex to signal that the execution rights are now transfered to the tracking thread */ - MutexLockHolder trackingFinishedLockHolder(*m_TrackingFinishedMutex); // keep lock until end of scope + std::lock_guard lock(m_TrackingFinishedMutex); if (this->GetState() != Tracking) return; NDIErrorCode returnvalue; returnvalue = m_DeviceProtocol->TSTART(); if (returnvalue != NDIOKAY) return; bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here - this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking + this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking localStopTracking = this->m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); while ((this->GetState() == Tracking) && (localStopTracking == false)) { if (this->m_DataTransferMode == TX) { returnvalue = this->m_DeviceProtocol->TX(); if (!((returnvalue == NDIOKAY) || (returnvalue == NDICRCERROR) || (returnvalue == NDICRCDOESNOTMATCH))) // right now, do not stop on crc errors break; } else { returnvalue = this->m_DeviceProtocol->BX(); if (returnvalue != NDIOKAY) break; } /* Update the local copy of m_StopTracking */ - this->m_StopTrackingMutex->Lock(); + this->m_StopTrackingMutex.lock(); localStopTracking = m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); } /* StopTracking was called, thus the mode should be changed back to Ready now that the tracking loop has ended. */ returnvalue = m_DeviceProtocol->TSTOP(); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << "An error occured while tracking tools."; } return; // returning from this function (and ThreadStartTracking()) this will end the thread and transfer control back to main thread by releasing trackingFinishedLockHolder } void mitk::NDITrackingDevice::TrackMarkerPositions() { - MutexLockHolder trackingFinishedLockHolder(*m_TrackingFinishedMutex); // keep lock until end of scope + std::lock_guard lock(m_TrackingFinishedMutex); if (m_OperationMode == ToolTracking6D) return; if (this->GetState() != Tracking) return; NDIErrorCode returnvalue; returnvalue = m_DeviceProtocol->DSTART(); // Start Diagnostic Mode if (returnvalue != NDIOKAY) return; bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here - this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking + this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking localStopTracking = this->m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); while ((this->GetState() == Tracking) && (localStopTracking == false)) { - m_MarkerPointsMutex->Lock(); // lock points data structure + m_MarkerPointsMutex.lock(); // lock points data structure returnvalue = this->m_DeviceProtocol->POS3D(&m_MarkerPoints); // update points data structure with new position data from tracking device - m_MarkerPointsMutex->Unlock(); + m_MarkerPointsMutex.unlock(); if (!((returnvalue == NDIOKAY) || (returnvalue == NDICRCERROR) || (returnvalue == NDICRCDOESNOTMATCH))) // right now, do not stop on crc errors { std::cout << "Error in POS3D: could not read data. Possibly no markers present." << std::endl; } /* Update the local copy of m_StopTracking */ - this->m_StopTrackingMutex->Lock(); + this->m_StopTrackingMutex.lock(); localStopTracking = m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); itksys::SystemTools::Delay(1); } /* StopTracking was called, thus the mode should be changed back to Ready now that the tracking loop has ended. */ returnvalue = m_DeviceProtocol->DSTOP(); if (returnvalue != NDIOKAY) return; // how can this thread tell the application, that an error has occured? this->SetState(Ready); return; // returning from this function (and ThreadStartTracking()) this will end the thread } void mitk::NDITrackingDevice::TrackToolsAndMarkers() { - MutexLockHolder trackingFinishedLockHolder(*m_TrackingFinishedMutex); // keep lock until end of scope + std::lock_guard lock(m_TrackingFinishedMutex); if (m_OperationMode != HybridTracking) return; NDIErrorCode returnvalue; returnvalue = m_DeviceProtocol->TSTART(); // Start Diagnostic Mode if (returnvalue != NDIOKAY) return; bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here - this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking + this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking localStopTracking = this->m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); while ((this->GetState() == Tracking) && (localStopTracking == false)) { - m_MarkerPointsMutex->Lock(); // lock points data structure + m_MarkerPointsMutex.lock(); // lock points data structure returnvalue = this->m_DeviceProtocol->TX(true, &m_MarkerPoints); // update points data structure with new position data from tracking device - m_MarkerPointsMutex->Unlock(); + m_MarkerPointsMutex.unlock(); if (!((returnvalue == NDIOKAY) || (returnvalue == NDICRCERROR) || (returnvalue == NDICRCDOESNOTMATCH))) // right now, do not stop on crc errors { std::cout << "Error in TX: could not read data. Possibly no markers present." << std::endl; } /* Update the local copy of m_StopTracking */ - this->m_StopTrackingMutex->Lock(); + this->m_StopTrackingMutex.lock(); localStopTracking = m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); } /* StopTracking was called, thus the mode should be changed back to Ready now that the tracking loop has ended. */ returnvalue = m_DeviceProtocol->TSTOP(); if (returnvalue != NDIOKAY) return; // how can this thread tell the application, that an error has occurred? this->SetState(Ready); return; // returning from this function (and ThreadStartTracking()) this will end the thread } mitk::TrackingTool* mitk::NDITrackingDevice::GetTool(unsigned int toolNumber) const { - MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard lock(m_ToolsMutex); if (toolNumber < m_6DTools.size()) return m_6DTools.at(toolNumber); return nullptr; } mitk::TrackingTool* mitk::NDITrackingDevice::GetToolByName(std::string name) const { - MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard lock(m_ToolsMutex); auto end = m_6DTools.end(); for (auto iterator = m_6DTools.begin(); iterator != end; ++iterator) if (name.compare((*iterator)->GetToolName()) == 0) return *iterator; return nullptr; } mitk::NDIPassiveTool* mitk::NDITrackingDevice::GetInternalTool(std::string portHandle) { - MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard lock(m_ToolsMutex); auto end = m_6DTools.end(); for (auto iterator = m_6DTools.begin(); iterator != end; ++iterator) if (portHandle.compare((*iterator)->GetPortHandle()) == 0) return *iterator; return nullptr; } unsigned int mitk::NDITrackingDevice::GetToolCount() const { - MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard lock(m_ToolsMutex); return m_6DTools.size(); } bool mitk::NDITrackingDevice::Beep(unsigned char count) { if (this->GetState() != Setup) { return (m_DeviceProtocol->BEEP(count) == NDIOKAY); } else { return false; } } mitk::TrackingTool* mitk::NDITrackingDevice::AddTool(const char* toolName, const char* fileName, TrackingPriority p /*= NDIPassiveTool::Dynamic*/) { mitk::NDIPassiveTool::Pointer t = mitk::NDIPassiveTool::New(); if (t->LoadSROMFile(fileName) == false) return nullptr; t->SetToolName(toolName); t->SetTrackingPriority(p); if (this->InternalAddTool(t) == false) return nullptr; return t.GetPointer(); } bool mitk::NDITrackingDevice::InternalAddTool(mitk::NDIPassiveTool* tool) { if (tool == nullptr) return false; NDIPassiveTool::Pointer p = tool; /* if the connection to the tracking device is already established, add the new tool to the device now */ if (this->GetState() == Ready) { /* get a port handle for the tool */ std::string newPortHandle; NDIErrorCode returnvalue; returnvalue = m_DeviceProtocol->PHRQ(&newPortHandle); if (returnvalue == NDIOKAY) { p->SetPortHandle(newPortHandle.c_str()); /* now write the SROM file of the tool to the tracking system using PVWR */ returnvalue = m_DeviceProtocol->PVWR(&newPortHandle, p->GetSROMData(), p->GetSROMDataLength()); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not write SROM file for tool '") + p->GetToolName() + std::string("' to tracking device")).c_str(); } /* initialize the port handle */ returnvalue = m_DeviceProtocol->PINIT(&newPortHandle); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not initialize port '") + newPortHandle + std::string("' for tool '") + p->GetToolName() + std::string("'")).c_str(); } /* enable the port handle */ if (p->IsEnabled() == true) { returnvalue = m_DeviceProtocol->PENA(&newPortHandle, p->GetTrackingPriority()); // Enable tool if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not enable port '") + newPortHandle + std::string("' for tool '") + p->GetToolName() + std::string("'")).c_str(); } } } /* now that the tool is added to the device, add it to list too */ - m_ToolsMutex->Lock(); + m_ToolsMutex.lock(); this->m_6DTools.push_back(p); - m_ToolsMutex->Unlock(); + m_ToolsMutex.unlock(); this->Modified(); return true; } else if (this->GetState() == Setup) { /* In Setup mode, we only add it to the list, so that OpenConnection() can add it later */ - m_ToolsMutex->Lock(); + m_ToolsMutex.lock(); this->m_6DTools.push_back(p); - m_ToolsMutex->Unlock(); + m_ToolsMutex.unlock(); this->Modified(); return true; } else // in Tracking mode, no tools can be added return false; } bool mitk::NDITrackingDevice::RemoveTool(mitk::TrackingTool* tool) { mitk::NDIPassiveTool* ndiTool = dynamic_cast(tool); if (ndiTool == nullptr) return false; std::string portHandle = ndiTool->GetPortHandle(); /* a valid portHandle has length 2. If a valid handle exists, the tool is already added to the tracking device, so we have to remove it there if the connection to the tracking device has already been established. */ if ((portHandle.length() == 2) && (this->GetState() == Ready)) // do not remove a tool in tracking mode { NDIErrorCode returnvalue; returnvalue = m_DeviceProtocol->PHF(&portHandle); if (returnvalue != NDIOKAY) return false; /* Now that the tool is removed from the tracking device, remove it from our tool list too */ - MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex (scope is inside the if-block + std::lock_guard lock(m_ToolsMutex); auto end = m_6DTools.end(); for (auto iterator = m_6DTools.begin(); iterator != end; ++iterator) { if (iterator->GetPointer() == ndiTool) { m_6DTools.erase(iterator); this->Modified(); return true; } } return false; } else if (this->GetState() == Setup) // in Setup Mode, we are not connected to the tracking device, so we can just remove the tool from the tool list { - MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard lock(m_ToolsMutex); auto end = m_6DTools.end(); for (auto iterator = m_6DTools.begin(); iterator != end; ++iterator) { if ((*iterator).GetPointer() == ndiTool) { m_6DTools.erase(iterator); this->Modified(); return true; } } return false; } return false; } void mitk::NDITrackingDevice::InvalidateAll() { - MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard lock(m_ToolsMutex); auto end = m_6DTools.end(); for (auto iterator = m_6DTools.begin(); iterator != end; ++iterator) (*iterator)->SetDataValid(false); } bool mitk::NDITrackingDevice::SetOperationMode(OperationMode mode) { if (GetState() == Tracking) return false; m_OperationMode = mode; return true; } mitk::OperationMode mitk::NDITrackingDevice::GetOperationMode() { return m_OperationMode; } bool mitk::NDITrackingDevice::GetMarkerPositions(MarkerPointContainerType* markerpositions) { - m_MarkerPointsMutex->Lock(); + m_MarkerPointsMutex.lock(); *markerpositions = m_MarkerPoints; // copy the internal vector to the one provided - m_MarkerPointsMutex->Unlock(); + m_MarkerPointsMutex.unlock(); return (markerpositions->size() != 0); } bool mitk::NDITrackingDevice::DiscoverWiredTools() { /* First, check for disconnected tools and remove them */ this->FreePortHandles(); //NDI handling (PHSR 02, PINIT, PHSR 02, PHSR 00) => all initialized and all handles available //creation of MITK tools //NDI enable all tools (PENA) //NDI get all serial numbers (PHINF) /** NDI handling (PHSR 02, PINIT, PHSR 02, PHSR 00) => all initialized and all handles available **/ /* check for occupied port handles on channel 0 */ std::string portHandle; NDIErrorCode returnvalue = m_DeviceProtocol->PHSR(OCCUPIED, &portHandle); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << "Could not obtain a list of port handles that are connected on channel 0."; } /* Initialize all port handles on channel 0 */ for (unsigned int i = 0; i < portHandle.size(); i += 2) { std::string ph = portHandle.substr(i, 2); returnvalue = m_DeviceProtocol->PINIT(&ph); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not initialize port '") + ph + std::string(".")); } } /* check for occupied port handles on channel 1 (initialize automatically, portHandle is empty although additional tools were detected) */ //For a split port on a dual 5DOF tool, the first PHSR sent will report only one port handle. After the port handle is //initialized, it is assigned to channel 0. You must then use PHSR again to assign a port handle to channel 1. The //port handle for channel 1 is initialized automatically. returnvalue = m_DeviceProtocol->PHSR(OCCUPIED, &portHandle); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << "Could not obtain a list of port handles that are connected on channel 1."; } /* read all port handles */ returnvalue = m_DeviceProtocol->PHSR(ALL, &portHandle); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << "Could not obtain a list of port handles that are connected on all channels."; } /** 1. Create MITK tracking tool representations of NDI tools 2. NDI enable all tools (PENA) **/ for (unsigned int i = 0; i < portHandle.size(); i += 2) { std::string ph = portHandle.substr(i, 2); if (this->GetInternalTool(ph) != nullptr) // if we already have a tool with this handle continue; // then skip the initialization //define tracking priority auto trackingPriority = mitk::NDIPassiveTool::Dynamic; //instantiate an object for each tool that is connected mitk::NDIPassiveTool::Pointer newTool = mitk::NDIPassiveTool::New(); newTool->SetPortHandle(ph.c_str()); newTool->SetTrackingPriority(trackingPriority); //set a name for identification newTool->SetToolName((std::string("Port ") + ph).c_str()); /* enable the port handle */ returnvalue = m_DeviceProtocol->PENA(&ph, trackingPriority); // Enable tool if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << (std::string("Could not enable port '") + ph + std::string("' for tool '") + newTool->GetToolName() + std::string("'")).c_str(); } //we have to temporarily unlock m_ModeMutex here to avoid a deadlock with another lock inside InternalAddTool() if (this->InternalAddTool(newTool) == false) { mitkThrowException(mitk::IGTException) << "Error while adding new tool"; } } /** NDI get all serial numbers (PHINF) **/ // after initialization readout serial numbers of automatically detected tools for (unsigned int i = 0; i < portHandle.size(); i += 2) { std::string ph = portHandle.substr(i, 2); std::string portInfo; NDIErrorCode returnvaluePort = m_DeviceProtocol->PHINF(ph, &portInfo); if ((returnvaluePort == NDIOKAY) && (portInfo.size() > 31)) dynamic_cast(this->GetInternalTool(ph))->SetSerialNumber(portInfo.substr(23, 8)); MITK_INFO << "portInfo: " << portInfo; itksys::SystemTools::Delay(10); } return true; } mitk::NDIErrorCode mitk::NDITrackingDevice::FreePortHandles() { /* first search for port handles that need to be freed: e.g. because of a reset of the tracking system */ NDIErrorCode returnvalue = NDIOKAY; std::string portHandle; returnvalue = m_DeviceProtocol->PHSR(FREED, &portHandle); if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << "Could not obtain a list of port handles that need to be freed"; } /* if there are port handles that need to be freed, free them */ if (portHandle.empty() == true) return returnvalue; std::string ph; for (unsigned int i = 0; i < portHandle.size(); i += 2) { ph = portHandle.substr(i, 2); mitk::NDIPassiveTool* t = this->GetInternalTool(ph); if (t != nullptr) // if we have a tool for the port handle that needs to be freed { if (this->RemoveTool(t) == false) // remove it (this will free the port too) returnvalue = NDIERROR; } else // we don't have a tool, the port handle exists only in the tracking device { returnvalue = m_DeviceProtocol->PHF(&ph); // free it there // What to do if port handle could not be freed? This seems to be a non critical error if (returnvalue != NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << "Could not free all port handles"; } } } return returnvalue; } int mitk::NDITrackingDevice::GetMajorFirmwareRevisionNumber() { std::string revision; if (m_DeviceProtocol->APIREV(&revision) != mitk::NDIOKAY || revision.empty() || (revision.size() != 9)) { MITK_ERROR << "Could not receive firmware revision number!"; return 0; } const std::string majrevno = revision.substr(2, 3); //cut out "004" from "D.004.001" return std::atoi(majrevno.c_str()); } const char* mitk::NDITrackingDevice::GetFirmwareRevisionNumber() { static std::string revision; if (m_DeviceProtocol->APIREV(&revision) != mitk::NDIOKAY || revision.empty() || (revision.size() != 9)) { MITK_ERROR << "Could not receive firmware revision number!"; revision = ""; return revision.c_str(); } return revision.c_str(); } bool mitk::NDITrackingDevice::AutoDetectToolsAvailable() { if (this->GetType() == mitk::NDIAuroraTypeInformation::GetTrackingDeviceName()) { return true; } else { return false; } } bool mitk::NDITrackingDevice::AddSingleToolIsAvailable() { //For Aurora, only AutoDetecion or loading of toolStorage should be used. It is not possible to add a single tool. if (this->GetType() == mitk::NDIAuroraTypeInformation::GetTrackingDeviceName()) { return false; } //For Polaris, a single tool can be added, there is no autoDetection. else { return true; } } mitk::NavigationToolStorage::Pointer mitk::NDITrackingDevice::AutoDetectTools() { mitk::NavigationToolStorage::Pointer autoDetectedStorage = mitk::NavigationToolStorage::New(); if (this->GetType() == mitk::NDIAuroraTypeInformation::GetTrackingDeviceName()) { try { this->OpenConnection(); this->StartTracking(); } catch (mitk::Exception& e) { MITK_WARN << "Warning, can not auto-detect tools! (" << e.GetDescription() << ")"; return autoDetectedStorage; } for (unsigned int i = 0; i < this->GetToolCount(); i++) { //create a navigation tool with sphere as surface std::stringstream toolname; toolname << "AutoDetectedTool" << i; mitk::NavigationTool::Pointer newTool = mitk::NavigationTool::New(); newTool->SetSerialNumber(dynamic_cast(this->GetTool(i))->GetSerialNumber()); newTool->SetIdentifier(toolname.str()); newTool->SetTrackingDeviceType(mitk::NDIAuroraTypeInformation::GetTrackingDeviceName()); newTool->GetDataNode()->SetName(toolname.str()); autoDetectedStorage->AddTool(newTool); } this->StopTracking(); this->CloseConnection(); } return autoDetectedStorage; } bool mitk::NDITrackingDevice::GetSupportedVolumes(unsigned int* numberOfVolumes, mitk::NDITrackingDevice::NDITrackingVolumeContainerType* volumes, mitk::NDITrackingDevice::TrackingVolumeDimensionType* volumesDimensions) { if (numberOfVolumes == nullptr || volumes == nullptr || volumesDimensions == nullptr) return false; static std::string info; if (m_DeviceProtocol->SFLIST(&info) != mitk::NDIOKAY || info.empty()) { MITK_ERROR << "Could not receive tracking volume information of tracking system!"; return false; } /*info contains the following: (+n times:) */ (*numberOfVolumes) = (unsigned int)std::atoi(info.substr(0, 1).c_str()); for (unsigned int i = 0; i < (*numberOfVolumes); i++) { //e.g. for cube: "9-025000+025000-025000+025000-055000-005000+000000+000000+000000+00000011" //for dome: "A+005000+048000+005000+066000+000000+000000+000000+000000+000000+00000011" std::string::size_type offset, end; offset = (i * 73) + 1; end = 73 + (i * 73); std::string currentVolume = info.substr(offset, end);//i=0: from 1 to 73 characters; i=1: from 75 to 148 char; // if i>0 then we have a return statement infront if (i > 0) currentVolume = currentVolume.substr(1, currentVolume.size()); if (currentVolume.compare(0, 1, NDIPolarisTypeInformation::GetDeviceDataPolarisOldModel().HardwareCode) == 0) volumes->push_back(NDIPolarisTypeInformation::GetDeviceDataPolarisOldModel().Model); if (currentVolume.compare(0, 3, NDIPolarisTypeInformation::GetDeviceDataPolarisSpectra().HardwareCode) == 0) volumes->push_back(NDIPolarisTypeInformation::GetDeviceDataPolarisSpectra().Model); if (currentVolume.compare(1, 3, NDIPolarisTypeInformation::GetDeviceDataSpectraExtendedPyramid().HardwareCode) == 0) { currentVolume = currentVolume.substr(1, currentVolume.size()); volumes->push_back(NDIPolarisTypeInformation::GetDeviceDataSpectraExtendedPyramid().Model); } if (currentVolume.compare(0, 1, NDIPolarisTypeInformation::GetDeviceDataPolarisVicra().HardwareCode) == 0) volumes->push_back(NDIPolarisTypeInformation::GetDeviceDataPolarisVicra().Model); else if (currentVolume.compare(0, 1, mitk::NDIAuroraTypeInformation::GetDeviceDataAuroraPlanarCube().HardwareCode) == 0) volumes->push_back(mitk::NDIAuroraTypeInformation::GetDeviceDataAuroraPlanarCube().Model);//alias cube else if (currentVolume.compare(0, 1, mitk::NDIAuroraTypeInformation::GetDeviceDataAuroraPlanarDome().HardwareCode) == 0) volumes->push_back(mitk::NDIAuroraTypeInformation::GetDeviceDataAuroraPlanarDome().Model); //fill volumesDimensions for (unsigned int index = 0; index < 10; index++) { std::string::size_type offD, endD; offD = 1 + (index * 7); //7 digits per dimension and the first is the type of volume endD = offD + 7; int dimension = std::atoi(currentVolume.substr(offD, endD).c_str()); dimension /= 100; //given in mm. 7 digits are xxxx.xx according to NDI //strange, the last two digits (11) also for the metal flag get read also... volumesDimensions->push_back(dimension); } } return true; } bool mitk::NDITrackingDevice::SetVolume(mitk::TrackingDeviceData volume) { if (m_DeviceProtocol->VSEL(volume) != mitk::NDIOKAY) { mitkThrowException(mitk::IGTHardwareException) << "Could not set volume!"; } return true; } diff --git a/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.h b/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.h index 7a160e34e1..e38481e59e 100644 --- a/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.h +++ b/Modules/IGT/TrackingDevices/mitkNDITrackingDevice.h @@ -1,330 +1,329 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKNDITRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2 #define MITKNDITRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2 #include "mitkTrackingDevice.h" #include -#include -#include "itkFastMutexLock.h" +#include +#include #include #include "mitkNDIProtocol.h" #include "mitkNDIPassiveTool.h" #include "mitkSerialCommunication.h" namespace mitk { class NDIProtocol; /** Documentation * \brief superclass for specific NDI tracking Devices that use serial communication. * * implements the TrackingDevice interface for NDI tracking devices (POLARIS, AURORA) * * \ingroup IGT */ class MITKIGT_EXPORT NDITrackingDevice : public TrackingDevice { friend class NDIProtocol; public: typedef std::vector Tool6DContainerType; ///< List of 6D tools of the correct type for this tracking device typedef mitk::TrackingDeviceType NDITrackingDeviceType; ///< This enumeration includes the two types of NDI tracking devices (Polaris, Aurora). typedef mitk::SerialCommunication::PortNumber PortNumber; ///< Port number of the serial connection typedef mitk::SerialCommunication::BaudRate BaudRate; ///< Baud rate of the serial connection typedef mitk::SerialCommunication::DataBits DataBits; ///< Number of data bits used in the serial connection typedef mitk::SerialCommunication::Parity Parity; ///< Parity mode used in the serial connection typedef mitk::SerialCommunication::StopBits StopBits; ///< Number of stop bits used in the serial connection typedef mitk::SerialCommunication::HardwareHandshake HardwareHandshake; ///< Hardware handshake mode of the serial connection typedef mitk::NDIPassiveTool::TrackingPriority TrackingPriority; ///< Tracking priority used for tracking a tool mitkClassMacro(NDITrackingDevice, TrackingDevice); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * \brief Set the type of the NDI Tracking Device because it can not jet handle this itself */ //itkSetMacro(Type, TrackingDeviceType); /** * \brief initialize the connection to the tracking device * * OpenConnection() establishes the connection to the tracking device by: * - initializing the serial port with the given parameters (port number, baud rate, ...) * - connection to the tracking device * - initializing the device * - initializing all manually added passive tools (user supplied srom file) * - initializing active tools that are connected to the tracking device * @throw mitk::IGTHardwareException Throws an exception if there are errors while connecting to the device. * @throw mitk::IGTException Throws a normal IGT exception if an error occures which is not related to the hardware. */ bool OpenConnection() override; /** * \brief Closes the connection * * CloseConnection() resets the tracking device, invalidates all tools and then closes the serial port. */ bool CloseConnection() override; /** @throw mitk::IGTHardwareException Throws an exception if there are errors while connecting to the device. */ bool InitializeWiredTools(); /** Sets the rotation mode of this class. See documentation of enum RotationMode for details * on the different modes. */ void SetRotationMode(RotationMode r) override; /** * \brief TestConnection() tries to connect to a NDI tracking device on the current port/device and returns which device it has found * * TestConnection() tries to connect to a NDI tracking device on the current port/device. * \return It returns the type of the device that answers at the port/device. Throws an exception if no device is available on that port. * @throw mitk::IGTHardwareException Throws an exception if there are errors while connecting to the device. */ virtual mitk::TrackingDeviceType TestConnection(); /** * \brief retrieves all wired tools from the tracking device * * This method queries the tracking device for all wired tools, initializes them and creates TrackingTool representation objects * for them * \return True if the method was executed successful. * @throw mitk::IGTHardwareException Throws an exception if there are errors while connecting to the device. * @throw mitk::IGTException Throws a normal IGT exception if an error occures which is not related to the hardware. */ bool DiscoverWiredTools(); /** * \brief Start the tracking. * * A new thread is created, which continuously reads the position and orientation information of each tool and stores them inside the tools. * Depending on the current operation mode (see SetOperationMode()), either the 6D tools (ToolTracking6D), 5D tools (ToolTracking5D), * 3D marker positions (MarkerTracking3D) or both 6D tools and 3D markers (HybridTracking) are updated. * Call StopTracking() to stop the tracking thread. */ bool StartTracking() override; /** * \brief return the tool with index toolNumber */ TrackingTool* GetTool(unsigned int toolNumber) const override; mitk::TrackingTool* GetToolByName(std::string name) const override; /** * \brief return current number of tools */ unsigned int GetToolCount() const override; /** * \brief Create a passive 6D tool with toolName and fileName and add it to the list of tools * * This method will create a new NDIPassiveTool object, load the SROM file fileName, * set the tool name toolName and the tracking priority p and then add * it to the list of tools. It returns a pointer of type mitk::TrackingTool to the tool * that can be used to read tracking data from it. * This is the only way to add tools to NDITrackingDevice. * @throw mitk::IGTHardwareException Throws an exception if there are errors while adding the tool. * * \warning adding tools is not possible in tracking mode, only in setup and ready. */ mitk::TrackingTool* AddTool(const char* toolName, const char* fileName, TrackingPriority p = NDIPassiveTool::Dynamic); /** * \brief Remove a passive 6D tool from the list of tracked tools. * * \warning removing tools is not possible in tracking mode, only in setup and ready modes. */ virtual bool RemoveTool(TrackingTool* tool); /** * \brief reloads the srom file and reinitializes the tool */ virtual bool UpdateTool(mitk::TrackingTool* tool); virtual void SetPortNumber(const PortNumber _arg); ///< set port number for serial communication itkGetConstMacro(PortNumber, PortNumber); ///< returns the port number for serial communication virtual void SetDeviceName(std::string _arg); ///< set device name (e.g. COM1, /dev/ttyUSB0). If this is set, PortNumber will be ignored itkGetStringMacro(DeviceName); ///< returns the device name for serial communication virtual void SetBaudRate(const BaudRate _arg); ///< set baud rate for serial communication itkGetConstMacro(BaudRate, BaudRate); ///< returns the baud rate for serial communication virtual void SetDataBits(const DataBits _arg); ///< set number of data bits itkGetConstMacro(DataBits, DataBits); ///< returns the data bits for serial communication virtual void SetParity(const Parity _arg); ///< set parity mode itkGetConstMacro(Parity, Parity); ///< returns the parity mode virtual void SetStopBits(const StopBits _arg); ///< set number of stop bits itkGetConstMacro(StopBits, StopBits); ///< returns the number of stop bits virtual void SetHardwareHandshake(const HardwareHandshake _arg); ///< set use hardware handshake for serial communication itkGetConstMacro(HardwareHandshake, HardwareHandshake); ///< returns the hardware handshake setting virtual void SetIlluminationActivationRate(const IlluminationActivationRate _arg); ///< set activation rate of IR illumator for polaris itkGetConstMacro(IlluminationActivationRate, IlluminationActivationRate); ///< returns the activation rate of IR illumator for polaris virtual void SetDataTransferMode(const DataTransferMode _arg); ///< set data transfer mode to text (TX) or binary (BX). \warning: only TX is supportet at the moment itkGetConstMacro(DataTransferMode, DataTransferMode); ///< returns the data transfer mode virtual bool Beep(unsigned char count); ///< Beep the tracking device 1 to 9 times NDIErrorCode GetErrorCode(const std::string* input); ///< returns the error code for a string that contains an error code in hexadecimal format virtual bool SetOperationMode(OperationMode mode); ///< set operation mode to 6D tool tracking, 3D marker tracking or 6D&3D hybrid tracking (see OperationMode) virtual OperationMode GetOperationMode(); ///< get current operation mode /** * \brief Get 3D marker positions (operation mode must be set to MarkerTracking3D or HybridTracking) */ virtual bool GetMarkerPositions(MarkerPointContainerType* markerpositions); /** * \brief Get major revision number from tracking device * should not be called directly after starting to track **/ virtual int GetMajorFirmwareRevisionNumber(); /** * \brief Get revision number from tracking device as string * should not be called directly after starting to track **/ virtual const char* GetFirmwareRevisionNumber(); /** @return Returns true if this device can autodetects its tools. */ bool AutoDetectToolsAvailable() override; /** @return Returns true if it is possible to add a single tool. True for Polaris, false for Aurora.*/ bool AddSingleToolIsAvailable() override; /** Autodetects tools from this device and returns them as a navigation tool storage. * @return Returns the detected tools. Returns an empty storage if no tools are present * or if detection is not possible */ mitk::NavigationToolStorage::Pointer AutoDetectTools() override; protected: typedef std::vector NDITrackingVolumeContainerType; ///< vector of tracking volumes typedef std::vector TrackingVolumeDimensionType; ///< List of the supported tracking volume dimensions. /** * \brief Get number of supported tracking volumes, a vector containing the supported volumes and * a vector containing the signed dimensions in mm. For each volume 10 boundaries are stored in the order of * the supported volumes (see AURORA API GUIDE: SFLIST p.54). **/ virtual bool GetSupportedVolumes(unsigned int* numberOfVolumes, NDITrackingVolumeContainerType* volumes, TrackingVolumeDimensionType* volumesDimensions); /** * \brief Sets the desired tracking volume. Returns true if the volume type could be set. It is set in the OpenConnection() Method and sets the tracking volume out of m_Data. * @throw mitk::IGTHardwareException Throws an IGT hardware exception if the volume could not be set. **/ virtual bool SetVolume(mitk::TrackingDeviceData volume); /** * \brief Add a passive 6D tool to the list of tracked tools. This method is used by AddTool * @throw mitk::IGTHardwareException Throws an exception if there are errors while adding the tool. * \warning adding tools is not possible in tracking mode, only in setup and ready. */ virtual bool InternalAddTool(NDIPassiveTool* tool); /* Methods for NDIProtocol friend class */ virtual void InvalidateAll(); ///< invalidate all tools NDIPassiveTool* GetInternalTool(std::string portHandle); ///< returns the tool object that has been assigned the port handle or nullptr if no tool can be found /** * \brief free all port handles that need to be freed * * This method retrieves a list of all port handles that need to be freed (e.g. tool got disconnected) * and frees the handles at the tracking device and it removes the tools from the internal tool list * \warning This method can remove TrackingTools from the tool list! After calling this method, GetTool(i) could return * a different tool, because tool indices could have changed. * @throw mitk::IGTHardwareException Throws an exception if there are errors while communicating with the device. * \return returns NDIOKAY if everything was sucessfull, returns an error code otherwise */ NDIErrorCode FreePortHandles(); NDIErrorCode Send(const std::string* message, bool addCRC = true); ///< Send message to tracking device NDIErrorCode Receive(std::string* answer, unsigned int numberOfBytes); ///< receive numberOfBytes bytes from tracking device NDIErrorCode ReceiveByte(char* answer); ///< lightweight receive function, that reads just one byte NDIErrorCode ReceiveLine(std::string* answer); ///< receive characters until the first LF (The LF is included in the answer string) void ClearSendBuffer(); ///< empty send buffer of serial communication interface void ClearReceiveBuffer(); ///< empty receive buffer of serial communication interface const std::string CalcCRC(const std::string* input); ///< returns the CRC16 for input as a std::string public: /** * \brief TrackTools() continuously polls serial interface for new 6d tool positions until StopTracking is called. * * Continuously tracks the 6D position of all tools until StopTracking() is called. * This function is executed by the tracking thread (through StartTracking() and ThreadStartTracking()). * It should not be called directly. * @throw mitk::IGTHardwareException Throws an exception if there are errors while tracking the tools. */ virtual void TrackTools(); /** * \brief continuously polls serial interface for new 3D marker positions until StopTracking is called. * * Continuously tracks the 3D position of all markers until StopTracking() is called. * This function is executed by the tracking thread (through StartTracking() and ThreadStartTracking()). * It should not be called directly. */ virtual void TrackMarkerPositions(); /** * \brief continuously polls serial interface for new 3D marker positions and 6D tool positions until StopTracking is called. * * Continuously tracks the 3D position of all markers and the 6D position of all tools until StopTracking() is called. * This function is executed by the tracking thread (through StartTracking() and ThreadStartTracking()). * It should not be called directly. */ virtual void TrackToolsAndMarkers(); /** - * \brief static start method for the tracking thread. + * \brief start method for the tracking thread. */ - static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* data); + void ThreadStartTracking(); protected: NDITrackingDevice(); ///< Constructor ~NDITrackingDevice() override; ///< Destructor std::string m_DeviceName;///< Device Name PortNumber m_PortNumber; ///< COM Port Number BaudRate m_BaudRate; ///< COM Port Baud Rate DataBits m_DataBits; ///< Number of Data Bits per token Parity m_Parity; ///< Parity mode for communication StopBits m_StopBits; ///< number of stop bits per token HardwareHandshake m_HardwareHandshake; ///< use hardware handshake for serial port connection ///< which tracking volume is currently used (if device supports multiple volumes) (\warning This parameter is not used yet) IlluminationActivationRate m_IlluminationActivationRate; ///< update rate of IR illuminator for Polaris DataTransferMode m_DataTransferMode; ///< use TX (text) or BX (binary) (\warning currently, only TX mode is supported) Tool6DContainerType m_6DTools; ///< list of 6D tools - itk::FastMutexLock::Pointer m_ToolsMutex; ///< mutex for coordinated access of tool container + mutable std::mutex m_ToolsMutex; ///< mutex for coordinated access of tool container mitk::SerialCommunication::Pointer m_SerialCommunication; ///< serial communication interface - itk::FastMutexLock::Pointer m_SerialCommunicationMutex; ///< mutex for coordinated access of serial communication interface + std::mutex m_SerialCommunicationMutex; ///< mutex for coordinated access of serial communication interface NDIProtocol::Pointer m_DeviceProtocol; ///< create and parse NDI protocol strings - itk::MultiThreader::Pointer m_MultiThreader; ///< creates tracking thread that continuously polls serial interface for new tracking data - int m_ThreadID; ///< ID of tracking thread + std::thread m_Thread; ///< ID of tracking thread OperationMode m_OperationMode; ///< tracking mode (6D tool tracking, 3D marker tracking,...) - itk::FastMutexLock::Pointer m_MarkerPointsMutex; ///< mutex for marker point data container + std::mutex m_MarkerPointsMutex; ///< mutex for marker point data container MarkerPointContainerType m_MarkerPoints; ///< container for markers (3D point tracking mode) }; } // namespace mitk #endif /* MITKNDITRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2 */ diff --git a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.cpp index b9fe449077..9b78fca04a 100644 --- a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.cpp +++ b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.cpp @@ -1,504 +1,501 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkOpenIGTLinkTrackingDevice.h" #include "mitkOpenIGTLinkTrackingTool.h" #include "mitkIGTConfig.h" #include "mitkIGTTimeStamp.h" #include "mitkIGTHardwareException.h" #include "mitkTrackingTypes.h" #include #include -#include #include #include #include //sleep headers #include #include -typedef itk::MutexLockHolder MutexLockHolder; - mitk::OpenIGTLinkTrackingDevice::OpenIGTLinkTrackingDevice() : mitk::TrackingDevice(), m_UpdateRate(60) { //set the type of this tracking device this->m_Data = mitk::OpenIGTLinkTypeInformation::GetDeviceDataOpenIGTLinkTrackingDeviceConnection(); m_OpenIGTLinkClient = mitk::IGTLClient::New(true); m_OpenIGTLinkClient->SetName("OpenIGTLink Tracking Device"); m_OpenIGTLinkClient->EnableNoBufferingMode(false); m_IGTLDeviceSource = mitk::IGTLTrackingDataDeviceSource::New(); m_IGTLDeviceSource->SetIGTLDevice(m_OpenIGTLinkClient); } mitk::OpenIGTLinkTrackingDevice::~OpenIGTLinkTrackingDevice() { } int mitk::OpenIGTLinkTrackingDevice::GetPortNumber() { return m_OpenIGTLinkClient->GetPortNumber(); } bool mitk::OpenIGTLinkTrackingDevice::AutoDetectToolsAvailable() { return true; } mitk::NavigationToolStorage::Pointer mitk::OpenIGTLinkTrackingDevice::AutoDetectTools() { mitk::NavigationToolStorage::Pointer returnValue = mitk::NavigationToolStorage::New(); if (m_OpenIGTLinkClient->GetPortNumber() == -1) { MITK_WARN << "Connection not initialized, aborting (invalid port number)."; return mitk::NavigationToolStorage::New(); } //open connection try { m_IGTLDeviceSource->Connect(); m_IGTLDeviceSource->StartCommunication(); } catch (std::runtime_error &e) { MITK_WARN << "AutoDetection: Open IGT Link device retruned an error while trying to connect: " << e.what(); return mitk::NavigationToolStorage::New(); } //get a message to find out type m_IGTLDeviceSource->SettrackingDataType(mitk::IGTLTrackingDataDeviceSource::UNKNOWN); mitk::IGTLMessage::Pointer receivedMessage = ReceiveMessage(100); const char* msgType = receivedMessage->GetIGTLMessageType(); if (std::string(msgType).empty()) { MITK_INFO << "Did not receive a message. Do you have to start the stream manually at the server?"; MITK_INFO << "Waiting for 10 seconds ..."; receivedMessage = ReceiveMessage(10000); msgType = receivedMessage->GetIGTLMessageType(); } MITK_INFO << "################# got message type: " << msgType; mitk::OpenIGTLinkTrackingDevice::TrackingMessageType type = GetMessageTypeFromString(msgType); switch (type) { case UNKNOWN: m_IGTLDeviceSource->SettrackingDataType(mitk::IGTLTrackingDataDeviceSource::UNKNOWN); break; case TDATA: m_IGTLDeviceSource->SettrackingDataType(mitk::IGTLTrackingDataDeviceSource::TDATA); break; case QTDATA: m_IGTLDeviceSource->SettrackingDataType(mitk::IGTLTrackingDataDeviceSource::QTDATA); break; case TRANSFORM: m_IGTLDeviceSource->SettrackingDataType(mitk::IGTLTrackingDataDeviceSource::TRANSFORM); break; } returnValue = DiscoverToolsAndConvertToNavigationTools(type); //close connection try { m_IGTLDeviceSource->StopCommunication(); m_IGTLDeviceSource->Disconnect(); } catch (std::runtime_error &e) { MITK_WARN << "AutoDetection: Open IGT Link device retruned an error while trying to disconnect: " << e.what(); return mitk::NavigationToolStorage::New(); } return returnValue; } mitk::NavigationToolStorage::Pointer mitk::OpenIGTLinkTrackingDevice::DiscoverToolsAndConvertToNavigationTools(mitk::OpenIGTLinkTrackingDevice::TrackingMessageType type, int NumberOfMessagesToWait) { MITK_INFO << "Start discovering tools by " << type << " messages"; mitk::NavigationToolStorage::Pointer returnValue = mitk::NavigationToolStorage::New(); std::map toolNameMap; for (int j = 0; jUpdate(); switch (type) { case TRANSFORM: { igtl::TransformMessage::Pointer msg = dynamic_cast(m_IGTLDeviceSource->GetOutput()->GetMessage().GetPointer()); if (msg == nullptr || msg.IsNull()) { MITK_INFO << "Received message is invalid / null. Skipping.."; continue; } int count = toolNameMap[msg->GetDeviceName()]; if (count == 0) { toolNameMap[msg->GetDeviceName()] = 1; } else { toolNameMap[msg->GetDeviceName()]++; } } break; case TDATA: { igtl::TrackingDataMessage::Pointer msg = dynamic_cast(m_IGTLDeviceSource->GetOutput()->GetMessage().GetPointer()); if (msg == nullptr || msg.IsNull()) { MITK_INFO << "Received message is invalid / null. Skipping.."; continue; } for (int k = 0; k < msg->GetNumberOfTrackingDataElements(); k++) { igtl::TrackingDataElement::Pointer tde; msg->GetTrackingDataElement(k, tde); if (tde.IsNotNull()) { int count = toolNameMap[tde->GetName()]; if (count == 0) { toolNameMap[tde->GetName()] = 1; } else { toolNameMap[tde->GetName()]++; } } } } break; default: MITK_WARN << "Only TRANSFORM and TDATA is currently supported, skipping!"; break; } } int i = 0; for (std::map::iterator it = toolNameMap.begin(); it != toolNameMap.end(); ++it) { MITK_INFO << "Found tool: " << it->first; std::stringstream name; name << it->first; std::stringstream identifier; identifier << "AutoDetectedTool-" << i; i++; mitk::NavigationTool::Pointer newTool = ConstructDefaultOpenIGTLinkTool(name.str(), identifier.str()); returnValue->AddTool(newTool); } return returnValue; } std::string mitk::OpenIGTLinkTrackingDevice::GetHostname() { return m_OpenIGTLinkClient->GetHostname(); } void mitk::OpenIGTLinkTrackingDevice::SetPortNumber(int portNumber) { m_OpenIGTLinkClient->SetPortNumber(portNumber); } void mitk::OpenIGTLinkTrackingDevice::SetHostname(std::string hostname) { m_OpenIGTLinkClient->SetHostname(hostname); } bool mitk::OpenIGTLinkTrackingDevice::IsDeviceInstalled() { return true; } mitk::TrackingTool* mitk::OpenIGTLinkTrackingDevice::AddTool(const char*, const char*) { mitk::OpenIGTLinkTrackingTool::Pointer t;// = mitk::OpenIGTLinkTrackingTool::New(); //TODO: Implement if (this->InternalAddTool(t) == false) return nullptr; return t.GetPointer(); } bool mitk::OpenIGTLinkTrackingDevice::InternalAddTool(OpenIGTLinkTrackingTool::Pointer tool) { m_AllTools.push_back(tool); return true; } bool mitk::OpenIGTLinkTrackingDevice::DiscoverTools(int waitingTime) { if (m_OpenIGTLinkClient->GetPortNumber() == -1) { MITK_WARN << "Connection not initialized, aborting (invalid port number)."; return false; } try { m_IGTLDeviceSource->Connect(); m_IGTLDeviceSource->StartCommunication(); } catch (std::runtime_error &e) { MITK_WARN << "Open IGT Link device retruned an error while trying to connect: " << e.what(); return false; } mitk::IGTLMessage::Pointer receivedMessage = ReceiveMessage(waitingTime); //check the tracking stream for the number and type of tools //igtl::MessageBase::Pointer receivedMessage = m_OpenIGTLinkClient->GetNextMessage(); if (receivedMessage.IsNull()) { MITK_WARN << "No message was received. Is there really a server?"; return false; } else if (!receivedMessage->IsDataValid()) { MITK_WARN << "Received invalid message."; return false; } const char* msgType = receivedMessage->GetIGTLMessageType(); mitk::OpenIGTLinkTrackingDevice::TrackingMessageType type = GetMessageTypeFromString(msgType); mitk::NavigationToolStorage::Pointer foundTools = this->DiscoverToolsAndConvertToNavigationTools(type); if (foundTools.IsNull() || (foundTools->GetToolCount() == 0)) { return false; } for (unsigned int i = 0; i < foundTools->GetToolCount(); i++) { AddNewToolForName(foundTools->GetTool(i)->GetToolName(), i); } MITK_INFO << "Found tools: " << foundTools->GetToolCount(); return true; } mitk::IGTLMessage::Pointer mitk::OpenIGTLinkTrackingDevice::ReceiveMessage(int waitingTime) { mitk::IGTLMessage::Pointer receivedMessage; //send a message to the server: start tracking stream mitk::IGTLMessageFactory::Pointer msgFactory = m_OpenIGTLinkClient->GetMessageFactory(); std::string message[2] = {"STT_QTDATA","STT_TDATA"}; for (int i = 0; i < 2; i++) { igtl::MessageBase::Pointer sttMsg = msgFactory->CreateInstance(message[i]); //TODO: Fix this to dynamically get this from GUI ((igtl::StartTrackingDataMessage*)sttMsg.GetPointer())->SetResolution(m_UpdateRate); m_OpenIGTLinkClient->SendMessage(mitk::IGTLMessage::New(sttMsg)); } std::chrono::high_resolution_clock::time_point time = std::chrono::high_resolution_clock::now(); std::chrono::milliseconds d = std::chrono::milliseconds(waitingTime); while (!(receivedMessage.IsNotNull() && receivedMessage->IsDataValid())) { m_IGTLDeviceSource->Update(); receivedMessage = m_IGTLDeviceSource->GetOutput(); if ((time + d) < std::chrono::high_resolution_clock::now()) break; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return receivedMessage; } void mitk::OpenIGTLinkTrackingDevice::AddNewToolForName(std::string name, int i) { mitk::OpenIGTLinkTrackingTool::Pointer newTool = mitk::OpenIGTLinkTrackingTool::New(); if (name == "") //if no name was given create a default name { std::stringstream defaultName; defaultName << "OpenIGTLinkTool#" << i; name = defaultName.str(); } MITK_INFO << "Added tool " << name << " to tracking device."; newTool->SetToolName(name); InternalAddTool(newTool); } mitk::NavigationTool::Pointer mitk::OpenIGTLinkTrackingDevice::ConstructDefaultOpenIGTLinkTool(std::string name, std::string identifier) { mitk::NavigationTool::Pointer newTool = mitk::NavigationTool::New(); newTool->GetDataNode()->SetName(name); newTool->SetIdentifier(identifier); newTool->SetTrackingDeviceType(mitk::OpenIGTLinkTypeInformation::GetDeviceDataOpenIGTLinkTrackingDeviceConnection().Line); return newTool; } void mitk::OpenIGTLinkTrackingDevice::UpdateTools() { if (this->GetState() != Tracking) { MITK_ERROR << "Method was called in the wrong state, something went wrong!"; return; } m_IGTLMsgToNavDataFilter->Update(); for (std::size_t j = 0; j < m_IGTLMsgToNavDataFilter->GetNumberOfIndexedOutputs(); ++j) { mitk::NavigationData::Pointer currentNavData = m_IGTLMsgToNavDataFilter->GetOutput(j); const char* name = currentNavData->GetName(); for (std::size_t i = 0; i < m_AllTools.size(); i++) { if (strcmp(m_AllTools.at(i)->GetToolName(), name) == 0) { m_AllTools.at(i)->SetDataValid(currentNavData->IsDataValid()); m_AllTools.at(i)->SetPosition(currentNavData->GetPosition()); m_AllTools.at(i)->SetOrientation(currentNavData->GetOrientation()); m_AllTools.at(i)->SetIGTTimeStamp(currentNavData->GetIGTTimeStamp()); } } } } bool mitk::OpenIGTLinkTrackingDevice::StartTracking() { //check tracking state if (this->GetState() != Ready) { MITK_WARN << "Cannot start tracking, device is not ready!"; return false; } try { m_IGTLDeviceSource->StartCommunication(); //send a message to the server: start tracking stream mitk::IGTLMessageFactory::Pointer msgFactory = m_OpenIGTLinkClient->GetMessageFactory(); std::string message = "STT_TDATA"; //m_OpenIGTLinkClient->SendMessage(msgFactory->CreateInstance(message)); } catch (std::runtime_error &e) { MITK_WARN << "Open IGT Link device retruned an error while starting communication: " << e.what(); return false; } //create internal igtl pipeline m_IGTLMsgToNavDataFilter = mitk::IGTLMessageToNavigationDataFilter::New(); m_IGTLMsgToNavDataFilter->SetNumberOfExpectedOutputs(this->GetToolCount()); m_IGTLMsgToNavDataFilter->ConnectTo(m_IGTLDeviceSource); //connect itk events typedef itk::SimpleMemberCommand< mitk::OpenIGTLinkTrackingDevice > CurCommandType; CurCommandType::Pointer messageReceivedCommand = CurCommandType::New(); messageReceivedCommand->SetCallbackFunction(this, &mitk::OpenIGTLinkTrackingDevice::UpdateTools); m_MessageReceivedObserverTag = m_OpenIGTLinkClient->AddObserver(mitk::MessageReceivedEvent(), messageReceivedCommand); m_OpenIGTLinkClient->EnableNoBufferingMode(true); this->SetState(Tracking); return true; } bool mitk::OpenIGTLinkTrackingDevice::StopTracking() { //check tracking state if (this->GetState() != Tracking) { MITK_WARN << "Cannot open connection, device is already connected!"; return false; } m_OpenIGTLinkClient->RemoveObserver(m_MessageReceivedObserverTag); //disconnect itk events try { m_IGTLDeviceSource->StopCommunication(); } catch (std::runtime_error &e) { MITK_WARN << "Open IGT Link device retruned an error while stopping communication: " << e.what(); return false; } m_OpenIGTLinkClient->EnableNoBufferingMode(false); this->SetState(Ready); return true; } unsigned int mitk::OpenIGTLinkTrackingDevice::GetToolCount() const { return (unsigned int)this->m_AllTools.size(); } mitk::TrackingTool* mitk::OpenIGTLinkTrackingDevice::GetTool(unsigned int toolNumber) const { if (toolNumber >= this->GetToolCount()) return nullptr; else return this->m_AllTools[toolNumber]; } bool mitk::OpenIGTLinkTrackingDevice::OpenConnection() { //check tracking state if (this->GetState() != Setup) { MITK_WARN << "Cannot open connection, device is already connected!"; return false; } try { m_IGTLDeviceSource->Connect(); } catch (std::runtime_error &e) { MITK_WARN << "Open IGT Link device retruned an error while trying to connect: " << e.what(); return false; } this->SetState(Ready); return true; } bool mitk::OpenIGTLinkTrackingDevice::CloseConnection() { //check tracking state if (this->GetState() != Ready) { MITK_WARN << "Cannot close connection, device is in the wrong state!"; return false; } try { m_IGTLDeviceSource->Disconnect(); } catch (std::runtime_error &e) { MITK_WARN << "Open IGT Link device retruned an error while trying to disconnect: " << e.what(); return false; } this->SetState(Setup); return true; } std::vector mitk::OpenIGTLinkTrackingDevice::GetAllTools() { return this->m_AllTools; } mitk::OpenIGTLinkTrackingDevice::TrackingMessageType mitk::OpenIGTLinkTrackingDevice::GetMessageTypeFromString(const char* messageTypeString) { if (strcmp(messageTypeString, "TDATA") == 0) { return mitk::OpenIGTLinkTrackingDevice::TrackingMessageType::TDATA; } else if (strcmp(messageTypeString, "QTDATA") == 0) { return mitk::OpenIGTLinkTrackingDevice::TrackingMessageType::QTDATA; } else if (strcmp(messageTypeString, "TRANSFORM") == 0) { return mitk::OpenIGTLinkTrackingDevice::TrackingMessageType::TRANSFORM; } else { return mitk::OpenIGTLinkTrackingDevice::TrackingMessageType::UNKNOWN; } } diff --git a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.h b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.h index e13411d464..12f66a5cac 100644 --- a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.h +++ b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingDevice.h @@ -1,175 +1,174 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKOPENIGTLINKTRACKINGDEVICE_H_HEADER_INCLUDED_ #define MITKOPENIGTLINKTRACKINGDEVICE_H_HEADER_INCLUDED_ #include #include #include #include #include #include -#include #include #include #include #include "mitkIGTLTrackingDataDeviceSource.h" namespace mitk { /** Documentation: * \brief An object of this class represents the MicronTracker device. You can add tools to this * device, then open the connection and start tracking. The tracking device will then * continuously update the tool coordinates. * \ingroup IGT */ class MITKIGT_EXPORT OpenIGTLinkTrackingDevice : public TrackingDevice { public: mitkClassMacro(OpenIGTLinkTrackingDevice, TrackingDevice); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** Sets the port number for the Open IGT Link connection. Default value is -1 (invalid). */ void SetPortNumber(int portNumber); /** Sets the hostname for the Open IGT Link connection. Default value is 127.0.0.1 (localhost). */ void SetHostname(std::string hostname); int GetPortNumber(); std::string GetHostname(); /** * \brief Starts the tracking. * \return Returns true if the tracking is started. Throws an exception if an error occures. * @throw mitk::IGTHardwareException Throws an exception if there is an error during start tracking. */ bool StartTracking() override; /** * \brief Stops the tracking. * \return Returns true if the tracking is stopped. */ bool StopTracking() override; /** * \brief Opens the connection to the device. This have to be done before the tracking is started. * @throw mitk::IGTHardwareException Throws an exception if there is an error during open connection. */ bool OpenConnection() override; /** * \brief Closes the connection and clears all resources. */ bool CloseConnection() override; /** * \return Returns the number of tools which have been added to the device. */ unsigned int GetToolCount() const override; /** * \param toolNumber The number of the tool which should be given back. * \return Returns the tool which the number "toolNumber". Returns nullptr, if there is * no tool with this number. */ TrackingTool* GetTool(unsigned int toolNumber) const override; /** * \brief Discover the tools available from the connected OpenIGTLink device and adds these tools to this tracking device. Therefore, a connection * is opened, the tools are discovered and added. * \param WaitingTime Defines how long the method waits for an answer from the server (in milliseconds). Default value is 10000 (10 seconds). * \return Returns true if the connection was established and the tools were discovered successfully and - if at least one tool was found - were added to this device. * Retruns false if no valid connection is available. */ bool DiscoverTools(int WaitingTime = 10000); /** * \brief Create a new OpenIGTLink tool with toolName and fileName and add it to the list of tools * * Note that tools are usually provided by the OpenIGTLink connection. In most cases, the method DiscoverTools() should be used * instead which automatically finds the provided tools. If you use this method to manually add tools be sure that you add the * same number and type of tools that are provided by the connected device. Otherwise problems might occur when you try to start * tracking. */ mitk::TrackingTool* AddTool(const char* toolName, const char* fileName); /** @return Returns true if this device can autodetects its tools. */ bool AutoDetectToolsAvailable() override; /** Autodetects tools from the current OpenIGTLink connection and returns them as a navigation tool storage. * @return Returns the detected tools. Returns an empty storage if no tools are present * or if OpenIGTLink Connection is not possible */ mitk::NavigationToolStorage::Pointer AutoDetectTools() override; bool IsDeviceInstalled() override; itkSetMacro(UpdateRate, int); ///< Sets the update rate of the device in fps. Default value is 60 fps. itkGetConstMacro(UpdateRate, int); ///< Returns the update rate of the device in fps protected: OpenIGTLinkTrackingDevice(); ~OpenIGTLinkTrackingDevice() override; /** * \brief Adds a tool to the tracking device. * * \param tool The tool which will be added. * \return Returns true if the tool has been added, false otherwise. */ bool InternalAddTool(OpenIGTLinkTrackingTool::Pointer tool); /** Updates the tools from the open IGT link connection. Is called every time a message received event is invoked.*/ void UpdateTools(); unsigned long m_MessageReceivedObserverTag; /** Receives one message from the OpenIGTLink connection. Starts the tracking stream if required. */ mitk::IGTLMessage::Pointer ReceiveMessage(int waitingTime); /** * \return Returns all tools of the tracking device. */ std::vector GetAllTools(); //OpenIGTLink connection class mitk::IGTLClient::Pointer m_OpenIGTLinkClient; //OpenIGTLink pipeline mitk::IGTLTrackingDataDeviceSource::Pointer m_IGTLDeviceSource; mitk::IGTLMessageToNavigationDataFilter::Pointer m_IGTLMsgToNavDataFilter; std::vector m_AllTools; ///< vector holding all tools int m_UpdateRate; ///< holds the update rate in FPS (will be set automatically when the OpenIGTLink connection is established) private: enum TrackingMessageType { TDATA, TRANSFORM, QTDATA, UNKNOWN }; mitk::OpenIGTLinkTrackingDevice::TrackingMessageType GetMessageTypeFromString(const char* messageTypeString); /** Discovers tools from the OpenIGTLink connection and converts them to MITK navigation tool objects. @return Returns a navigation tool storage holding all found tools. Returns an empty storage if no tools were found or if there was an error.*/ mitk::NavigationToolStorage::Pointer DiscoverToolsAndConvertToNavigationTools(mitk::OpenIGTLinkTrackingDevice::TrackingMessageType type, int NumberOfMessagesToWait = 50); void AddNewToolForName(std::string name, int i); mitk::NavigationTool::Pointer ConstructDefaultOpenIGTLinkTool(std::string name, std::string identifier); }; }//mitk #endif /* MITKOpenIGTLinkTRACKINGDEVICE_H_HEADER_INCLUDED_ */ diff --git a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingTool.h b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingTool.h index 14d573c261..9b305f97ec 100644 --- a/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingTool.h +++ b/Modules/IGT/TrackingDevices/mitkOpenIGTLinkTrackingTool.h @@ -1,42 +1,41 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKOpenIGTLinkTrackingTOOL_H_HEADER_INCLUDED_ #define MITKOpenIGTLinkTrackingTOOL_H_HEADER_INCLUDED_ #include -#include namespace mitk { class OpenIGTLinkTrackingDevice; /** Documentation: * \brief An object of this class represents a OpenIGTLink tracking tool. * A tool has to be added to a tracking device which will then * continuously update the tool coordinates. * \ingroup IGT */ class MITKIGT_EXPORT OpenIGTLinkTrackingTool : public TrackingTool { public: friend class OpenIGTLinkTrackingTrackingDevice; mitkClassMacro(OpenIGTLinkTrackingTool, TrackingTool); itkFactorylessNewMacro(Self) protected: itkCloneMacro(Self) OpenIGTLinkTrackingTool(); ~OpenIGTLinkTrackingTool() override; }; }//mitk #endif // MITKOpenIGTLinkTrackingTOOL_H_HEADER_INCLUDED_ diff --git a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.cpp index bf4d819264..7ab6f3c9ad 100644 --- a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.cpp +++ b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.cpp @@ -1,766 +1,734 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkOptitrackTrackingDevice.h" #include #ifdef MITK_USE_OPTITRACK_TRACKER /** * \brief API library header for Optitrack Systems */ #include //======================================================= // Static method: IsDeviceInstalled //======================================================= bool mitk::OptitrackTrackingDevice::IsDeviceInstalled() { return true; } //======================================================= // Constructor //======================================================= mitk::OptitrackTrackingDevice::OptitrackTrackingDevice() : mitk::TrackingDevice(), m_initialized(false) { - // Set the MultiThread and Mutex - this->m_MultiThreader = itk::MultiThreader::New(); - this->m_ToolsMutex = itk::FastMutexLock::New(); - //Set the mitk device information SetData(mitk::DeviceDataNPOptitrack); //Clear List of tools this->m_AllTools.clear(); } //======================================================= // Destructor //======================================================= mitk::OptitrackTrackingDevice::~OptitrackTrackingDevice() { MITK_DEBUG << "Deleting OptitrackTrackingDevice"; int result; // If device is in Tracking mode, stop the Tracking firts if (this->GetState() == mitk::TrackingDevice::Tracking) { MITK_DEBUG << "OptitrackTrackingDevice in Tracking State -> Stopping Tracking"; result = this->StopTracking(); if(result == NPRESULT_SUCCESS){ MITK_INFO << "OptitrackTrackingDevice Stopped"; } else { MITK_INFO << "Error during Stopping"; mitkThrowException(mitk::IGTException) << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(result); } } // If device is Ready, Close the connection to device and release the memory if (this->GetState() == mitk::TrackingDevice::Ready) { MITK_DEBUG << "OptitrackTrackingDevice in Ready State -> Closing the Connection"; result = this->CloseConnection(); if(result) { MITK_INFO << "OptitrackTrackingDevice Connection closed"; } else { MITK_DEBUG << "Error during Closing Connection"; mitkThrowException(mitk::IGTException) << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(result); } } // Set the device off m_initialized = false; // Change State to Setup this->SetState(mitk::TrackingDevice::Setup); MITK_DEBUG <<"OptitrackTrackingDevice deleted successfully"; } //======================================================= // OpenConnection //======================================================= bool mitk::OptitrackTrackingDevice::OpenConnection() { // Not initialize the system twice. if(!m_initialized) { MITK_DEBUG << "Initialize Optitrack Tracking System"; if( this->InitializeCameras() ) { m_initialized = true; // Set the initialized variable to true this->SetState(mitk::TrackingDevice::Ready); if(this->m_calibrationPath.empty()){ MITK_INFO << "Numer of connected cameras = " << TT_CameraCount(); MITK_WARN << "Attention: No calibration File defined !!"; return m_initialized; } else { this->LoadCalibration(); } } else { m_initialized = false; // Set the initialized variable to false this->SetState(mitk::TrackingDevice::Setup); // Set the State to Setup MITK_INFO << "Device initialization failed. Device is still in setup state"; mitkThrowException(mitk::IGTException) << "Device initialization failed. Device is still in setup state"; } } //this->LoadCalibration(); return m_initialized; } //======================================================= // InitializeCameras //======================================================= bool mitk::OptitrackTrackingDevice::InitializeCameras() { MITK_DEBUG << "Initialize Optitrack"; int result; result = TT_Initialize(); // Initialize the cameras if(result == NPRESULT_SUCCESS) { MITK_DEBUG << "Optitrack Initialization Succeed"; return true; } else { MITK_DEBUG << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(result); // If not succeed after OPTITRACK_ATTEMPTS times launch exception MITK_INFO << "Optitrack Tracking System cannot be initialized \n" << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(result); mitkThrowException(mitk::IGTException) << "Optitrack Tracking System cannot be initialized \n" << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(result); return false; } } //======================================================= // LoadCalibration //======================================================= bool mitk::OptitrackTrackingDevice::LoadCalibration() { MITK_DEBUG << "Loading System Calibration"; int resultLoadCalibration; // Check the file path if(this->m_calibrationPath.empty()){ MITK_INFO << "Calibration Path is empty"; mitkThrowException(mitk::IGTException) << "Calibration Path is empty"; return false; } // Once the system is ready and Initialized , a calibration file is loaded. if(this->m_initialized) { for( int i=OPTITRACK_ATTEMPTS; i>0; i--) { resultLoadCalibration = TT_LoadCalibration(this->m_calibrationPath.c_str()); if(resultLoadCalibration != NPRESULT_SUCCESS) { MITK_DEBUG << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(resultLoadCalibration); MITK_DEBUG << "Trying again..."; } else { MITK_DEBUG << "Calibration file has been loaded successfully"; return true; } } MITK_INFO << "System cannot load a calibration file"; mitkThrowException(mitk::IGTException) << "System cannot load a calibration file"; } else { MITK_INFO << "System is not ready for load a calibration file because it has not been initialized yet"; mitkThrowException(mitk::IGTException) << "System is not ready for load a calibration file because it has not been initialized yet"; return false; } // Never reach this point return false; } //======================================================= // SetCalibrationPath //======================================================= void mitk::OptitrackTrackingDevice::SetCalibrationPath(std::string calibrationPath){ MITK_DEBUG << "SetcalibrationPath"; MITK_DEBUG << calibrationPath; // Check the file path if(calibrationPath.empty()) { MITK_INFO << "Calibration Path is empty"; //mitkThrowException(mitk::IGTException) << "Calibration Path is empty"; return; } this->m_calibrationPath = calibrationPath; MITK_INFO << "Calibration Path has been updated to: " << this->m_calibrationPath; return; } //======================================================= // CloseConnection //======================================================= bool mitk::OptitrackTrackingDevice::CloseConnection() { MITK_DEBUG << "CloseConnection"; int resultStop, resultShutdown; if(m_initialized) // Close connection if the System was initialized first { if(this->GetState() == mitk::TrackingDevice::Tracking) { MITK_DEBUG << "Device state: Tracking -> Stoping the Tracking"; resultStop = this->StopTracking(); //Stop tracking on close } this->SetState(mitk::OptitrackTrackingDevice::Setup); for( int i=OPTITRACK_ATTEMPTS; i>0; i--) { TT_ClearTrackableList(); resultShutdown = TT_Shutdown(); if(resultShutdown == NPRESULT_SUCCESS) { MITK_DEBUG << "System has been Shutdown Correctly"; Sleep(2000); return true; } else { MITK_DEBUG << "System cannot ShutDown now. Trying again..."; } } MITK_INFO << "System cannot ShutDown now"; mitkThrowException(mitk::IGTException) << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(resultShutdown); return false; } else { MITK_INFO << "System has not been initialized. Close connection cannot be done"; mitkThrowException(mitk::IGTException) << "System has not been initialized. Close connection cannot be done"; return false; } return false; } //======================================================= // StartTracking //======================================================= bool mitk::OptitrackTrackingDevice::StartTracking() { MITK_DEBUG << "StartTracking"; bool resultIsTrackableTracked; if (this->GetState() != mitk::TrackingDevice::Ready) { MITK_INFO << "System is not in State Ready -> Cannot StartTracking"; mitkThrowException(mitk::IGTException) << "System is not in State Ready -> Cannot StartTracking"; return false; } this->SetState(mitk::TrackingDevice::Tracking); // Change the m_StopTracking Variable to false - this->m_StopTrackingMutex->Lock(); + this->m_StopTrackingMutex.lock(); this->m_StopTracking = false; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); /****************************************************************************** ############################################################################### TODO: check the timestamp from the Optitrack API ############################################################################### ******************************************************************************/ mitk::IGTTimeStamp::GetInstance()->Start(this); // Launch multiThreader using the Function ThreadStartTracking that executes the TrackTools() method - m_ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); // start a new thread that executes the TrackTools() method + m_Thread = std::thread(&OptitrackTrackingDevice::ThreadStartTracking, this); // start a new thread that executes the TrackTools() method // Information for the user if(GetToolCount() == 0) MITK_INFO << "No tools are defined"; for ( int i = 0; i < GetToolCount(); ++i) // use mutexed methods to access tool container { resultIsTrackableTracked = TT_IsTrackableTracked(i); if(resultIsTrackableTracked) { MITK_DEBUG << "Trackable " << i << " is inside the Tracking Volume and it is Tracked"; } else { MITK_DEBUG << "Trackable " << i << " is not been tracked. Check if it is inside the Tracking volume"; } } return true; } //======================================================= // StopTracking //======================================================= bool mitk::OptitrackTrackingDevice::StopTracking() { MITK_DEBUG << "StopTracking"; if (this->GetState() == mitk::TrackingDevice::Tracking) // Only if the object is in the correct state { //Change the StopTracking value - m_StopTrackingMutex->Lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling - m_StopTrackingMutex->Unlock(); + m_StopTrackingMutex.lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling + m_StopTrackingMutex.unlock(); this->SetState(mitk::TrackingDevice::Ready); } else { - m_TrackingFinishedMutex->Unlock(); + m_TrackingFinishedMutex.unlock(); MITK_INFO << "System is not in State Tracking -> Cannot StopTracking"; mitkThrowException(mitk::IGTException) << "System is not in State Tracking -> Cannot StopTracking"; return false; } /****************************************************************************** ############################################################################### TODO: check the timestamp from the Optitrack API ############################################################################### ******************************************************************************/ mitk::IGTTimeStamp::GetInstance()->Stop(this); - m_TrackingFinishedMutex->Unlock(); + m_TrackingFinishedMutex.unlock(); return true; } //======================================================= // ThreadStartTracking //======================================================= -ITK_THREAD_RETURN_TYPE mitk::OptitrackTrackingDevice::ThreadStartTracking(void* pInfoStruct) +void mitk::OptitrackTrackingDevice::ThreadStartTracking() { MITK_DEBUG << "ThreadStartTracking"; - - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - - if (pInfo == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - - OptitrackTrackingDevice *trackingDevice = static_cast(pInfo->UserData); - - if (trackingDevice != nullptr) - { - // Call the TrackTools function in this thread - trackingDevice->TrackTools(); - } - else - { - mitkThrowException(mitk::IGTException) << "In ThreadStartTracking(): trackingDevice is nullptr"; - } - - trackingDevice->m_ThreadID = -1; // reset thread ID because we end the thread here - return ITK_THREAD_RETURN_VALUE; + this->TrackTools(); } //======================================================= // GetOptitrackTool //======================================================= mitk::OptitrackTrackingTool* mitk::OptitrackTrackingDevice::GetOptitrackTool( unsigned int toolNumber) const { MITK_DEBUG << "ThreadStartTracking"; OptitrackTrackingTool* t = nullptr; - MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard toolsMutexLockHolder(m_ToolsMutex); // lock and unlock the mutex if(toolNumber < m_AllTools.size()) { t = m_AllTools.at(toolNumber); } else { MITK_INFO << "The tool numbered " << toolNumber << " does not exist"; mitkThrowException(mitk::IGTException) << "The tool numbered " << toolNumber << " does not exist"; } return t; } //======================================================= // GetToolCount //======================================================= unsigned int mitk::OptitrackTrackingDevice::GetToolCount() const { MITK_DEBUG << "GetToolCount"; - MutexLockHolder lock(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard lock(m_ToolsMutex); // lock and unlock the mutex return ( int)(this->m_AllTools.size()); } //======================================================= // TrackTools //======================================================= void mitk::OptitrackTrackingDevice::TrackTools() { MITK_DEBUG << "TrackTools"; Point3D position; ScalarType t = 0.0; try { bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here - this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking + this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking localStopTracking = this->m_StopTracking; /* lock the TrackingFinishedMutex to signal that the execution rights are now transfered to the tracking thread */ if (!localStopTracking) { - m_TrackingFinishedMutex->Lock(); + m_TrackingFinishedMutex.lock(); } - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); while ((this->GetState() == mitk::TrackingDevice::Tracking) && (localStopTracking == false)) { // For each Tracked Tool update the position and orientation for ( int i = 0; i < GetToolCount(); ++i) // use mutexed methods to access tool container { OptitrackTrackingTool* currentTool = this->GetOptitrackTool(i); if(currentTool != nullptr) { currentTool->updateTool(); MITK_DEBUG << "Tool number " << i << " updated position"; } else { MITK_DEBUG << "Get data from tool number " << i << " failed"; mitkThrowException(mitk::IGTException) << "Get data from tool number " << i << " failed"; } } /* Update the local copy of m_StopTracking */ - this->m_StopTrackingMutex->Lock(); + this->m_StopTrackingMutex.lock(); localStopTracking = m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); Sleep(OPTITRACK_FRAME_RATE); } // tracking ends if we pass this line - m_TrackingFinishedMutex->Unlock(); // transfer control back to main thread + m_TrackingFinishedMutex.unlock(); // transfer control back to main thread } catch(...) { - m_TrackingFinishedMutex->Unlock(); + m_TrackingFinishedMutex.unlock(); this->StopTracking(); mitkThrowException(mitk::IGTException) << "Error while trying to track tools. Thread stopped."; } } //======================================================= // SetCameraParams //======================================================= bool mitk::OptitrackTrackingDevice::SetCameraParams(int exposure, int threshold , int intensity, int videoType ) { MITK_DEBUG << "SetCameraParams"; if(this->m_initialized) { int num_cams = 0; int resultUpdate; bool resultSetCameraSettings; for( int i=OPTITRACK_ATTEMPTS; i>0; i--) { resultUpdate = TT_Update(); // Get Update for the Optitrack API if(resultUpdate == NPRESULT_SUCCESS) { MITK_DEBUG << "Update Succeed"; num_cams = TT_CameraCount(); i = 0; } else { MITK_DEBUG << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(resultUpdate); MITK_DEBUG << "Trying again..."; Sleep(30); } } // If no cameras are connected if(num_cams == 0) { MITK_DEBUG << "No cameras are connected to the device"; return false; mitkThrowException(mitk::IGTException) << "No cameras are connected to the device"; } for(int cam = 0; cam < num_cams; cam++) // for all connected cameras { for( int i=OPTITRACK_ATTEMPTS; i>0; i--) { resultUpdate = TT_Update(); // Get Update for the Optitrack API if(resultUpdate == NPRESULT_SUCCESS) { MITK_DEBUG << "Update Succeed for camera number " << cam; resultSetCameraSettings = TT_SetCameraSettings(cam,videoType,exposure,threshold,intensity); if(resultSetCameraSettings) { MITK_INFO << "Camera # "< System is not ready to perform the Camera Parameters Setting"; mitkThrowException(mitk::IGTException) << "System is not Initialized -> System is not ready to perform the Camera Parameters Setting"; return false; } return true; } //======================================================= // GetTool //======================================================= mitk::TrackingTool* mitk::OptitrackTrackingDevice::GetTool(unsigned int toolNumber) const { return static_cast(GetOptitrackTool(toolNumber)); } //======================================================= // AddToolByFileName //======================================================= bool mitk::OptitrackTrackingDevice::AddToolByDefinitionFile(std::string fileName) { bool resultSetToolByFileName; if(m_initialized) { OptitrackTrackingTool::Pointer t = OptitrackTrackingTool::New(); resultSetToolByFileName= t->SetToolByFileName(fileName); if(resultSetToolByFileName) { this->m_AllTools.push_back(t); MITK_INFO << "Added tool "<GetToolName()<< ". Tool vector size is now: "< Cannot Add tools"; mitkThrowException(mitk::IGTException) << "System is not Initialized -> Cannot Add tools"; return false; } } //======================================================= // IF Optitrack is not installed set functions to warnings //======================================================= #else //======================================================= // Static method: IsDeviceInstalled //======================================================= bool mitk::OptitrackTrackingDevice::IsDeviceInstalled() { return false; } //======================================================= // Constructor //======================================================= mitk::OptitrackTrackingDevice::OptitrackTrackingDevice() : mitk::TrackingDevice(), m_initialized(false) { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); } //======================================================= // Destructor //======================================================= mitk::OptitrackTrackingDevice::~OptitrackTrackingDevice() { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); } //======================================================= // OpenConnection //======================================================= bool mitk::OptitrackTrackingDevice::OpenConnection() { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); return false; } //======================================================= // InitializeCameras //======================================================= bool mitk::OptitrackTrackingDevice::InitializeCameras() { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); return false; } //======================================================= // LoadCalibration //======================================================= bool mitk::OptitrackTrackingDevice::LoadCalibration() { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); return false; } //======================================================= // SetcalibrationPath //======================================================= void mitk::OptitrackTrackingDevice::SetCalibrationPath(std::string /*calibrationPath*/) { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); } //======================================================= // CloseConnection //======================================================= bool mitk::OptitrackTrackingDevice::CloseConnection() { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); return false; } //======================================================= // StartTracking //======================================================= bool mitk::OptitrackTrackingDevice::StartTracking() { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); return false; } //======================================================= // StopTracking //======================================================= bool mitk::OptitrackTrackingDevice::StopTracking() { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); return false; } //======================================================= // ThreadStartTracking //======================================================= -ITK_THREAD_RETURN_TYPE mitk::OptitrackTrackingDevice::ThreadStartTracking(void*) +void mitk::OptitrackTrackingDevice::ThreadStartTracking() { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); - return 0; } //======================================================= // GetOptitrackTool //======================================================= mitk::OptitrackTrackingTool* mitk::OptitrackTrackingDevice::GetOptitrackTool(unsigned int) const { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); return nullptr; } //======================================================= // GetToolCount //======================================================= unsigned int mitk::OptitrackTrackingDevice::GetToolCount() const { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); return 0; } //======================================================= // TrackTools //======================================================= void mitk::OptitrackTrackingDevice::TrackTools() { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); } //======================================================= // SetCameraParams //======================================================= bool mitk::OptitrackTrackingDevice::SetCameraParams(int, int, int, int) { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); return false; } //======================================================= // GetTool //======================================================= mitk::TrackingTool* mitk::OptitrackTrackingDevice::GetTool(unsigned int) const { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); return nullptr; } //======================================================= // AddToolByFileName //======================================================= bool mitk::OptitrackTrackingDevice::AddToolByDefinitionFile(std::string) { MITK_WARN("IGT") << "Error: " << mitk::OptitrackErrorMessages::GetOptitrackErrorMessage(100); return false; } #endif diff --git a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.h b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.h index a575d23c11..2ddbc7cfa8 100644 --- a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.h +++ b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingDevice.h @@ -1,320 +1,301 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef OptitrackTrackingDevice_H_HEADER_INCLUDED #define OptitrackTrackingDevice_H_HEADER_INCLUDED #include #include -#include #include #include -#include #include -#include #include #include #include #include +#include +#include /** * \brief IGT Exceptions */ #include "mitkIGTIOException.h" #include "mitkIGTTimeStamp.h" #include "mitkIGTException.h" /** * \brief OptitrackTrackingTools */ #include "mitkOptitrackTrackingTool.h" - -/** -* \brief MutexHolder to keep rest of Mutex -*/ -typedef itk::MutexLockHolder MutexLockHolder; - - - - namespace mitk { /** Documentation: * \brief An object of this class represents the Optitrack device. You can add tools to this * device, then open the connection and start tracking. The tracking device will then * continuously update the tool coordinates. Remember that it will be necessary to * to have a license for using the Optitrack System. * See http://www.naturalpoint.com/ for details. * \author E. Marinetto (emarinetto@hggm.es) Instituto de Investigación Sanitaria Gregorio Marañón, Madrid, Spain. & M. Noll (matthias.noll@igd.fraunhofer.de) Cognitive Computing & Medical Imaging | Fraunhofer IGD * \ingroup IGT */ class MITKIGT_EXPORT OptitrackTrackingDevice : public mitk::TrackingDevice { friend class OptitrackTrackingTool; public: mitkClassMacro(OptitrackTrackingDevice, mitk::TrackingDevice); itkNewMacro(Self); /** * @returns Returns true if the Optitrack tracker is installed on this build (means activated in CMAKE). False if not. */ bool IsDeviceInstalled() override; // Define the Type of Tracker as DefinitionOfTool (MITK) typedef mitk::TrackingDeviceType OptiTrackTrackingDeviceType; /** * \brief Open the Connection with the Tracker. Calls LoadCalibration function and set the system up with the calibration file. * Remember that you have to set a calibration file first to open a correct connection to the Optical Tracking System. * \return Returns true if the connection is well done. Throws an exception if an error occures related to the Optitrack API messages. * @throw mitk::IGTException Throws an exception if InitializeCameras or LoadCalibration failed. */ bool OpenConnection() override; /** * \brief Close the Connection with the Tracker. Also CleanUp the Optitrack variables using the API: TT_CleanUp and TT_ShutDown. * Sometimes API does not work properly and some problems during the Clean Up has been reported. * \return Returns true if the cleaning up and shutdown worked correctly. Throws an exception if an error occures related to the Optitrack API messages. * @throw mitk::IGTException Throws an exception if the System cannot ShutDown now or was not initialized. */ bool CloseConnection() override; /** * \brief Start to Track the tools already defined. If no tools are defined for this tracker, it returns an error. * Tools can be added using either AddToolByDescriptionFile or AddToolsByConfigurationFiles * \return Returns true at least one tool was defined and the tracking is correct * @throw mitk::IGTException Throws an exception if the System is not in State Ready . */ bool StartTracking() override; /** * \brief Stop the Tracking Thread and tools will not longer be updated. * \return Returns true if Tracking thread could be stopped. * @throw mitk::IGTException Throws an exception if System is not in State Tracking. */ bool StopTracking() override; /** * \brief Return the tool pointer of the tool number toolNumber * \param toolNumber The number of the tool which should be given back. * \return Returns the tool which the number "toolNumber". Returns nullptr, if there is * no tool with this number. */ TrackingTool* GetTool(unsigned int toolNumber) const override; /** * \brief Return the tool pointer of the tool number toolNumber * \param toolNumber The number of the tool which should be given back. * \return Returns the tool which the number "toolNumber". Returns nullptr, if there is * no tool with this number. * @throw mitk::IGTException Throws an exception if there is the required tool does not exist. */ OptitrackTrackingTool* GetOptitrackTool(unsigned int toolNumber) const; /** * \brief Returns the number of defined tools * \return Returns the number of defined tools in the Optitrack device. */ unsigned int GetToolCount() const override; /** @brief Sets the directory where the calibration file of the MicronTracker can be found. */ itkSetMacro(Exp,int); /** @brief Gets the current calibration directory. */ itkGetMacro(Exp,int); /** @brief Sets the directory where the calibration file of the MicronTracker can be found. */ itkSetMacro(Led,int); /** @brief Gets the current calibration directory. */ itkGetMacro(Led,int); /** @brief Sets the directory where the calibration file of the MicronTracker can be found. */ itkSetMacro(Thr,int); /** @brief Gets the current calibration directory. */ itkGetMacro(Thr,int); /** @brief Sets the file where the calibration of the OptitrackTracker can be found. */ void SetCalibrationPath(std::string calibrationPath); /** @brief Gets the current calibration file. */ itkGetMacro(calibrationPath,std::string); /** * \brief Start the Tracking Thread for the tools - * @throw mitk::IGTException Throws an exception if variable trackingDevice is nullptr */ - static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* data); + void ThreadStartTracking(); /** * \brief Update each tool location in the list m_AllTools * @throw mitk::IGTException Throws an exception if the getting data operation failed for a defined tool */ void TrackTools(); /** * \brief Load the Calibration file to the Optitrack System and set the cameras in calibrated locations * \return Returns true if the calibration was uploaded correctly * @throw mitk::IGTException Throws an exception if Calibration Path is empty, the System cannot load a calibration file or System is not ready for load a calibration file because it has not been initialized yet */ bool LoadCalibration(); /** * \brief Set the Cameras Exposure, Threshold and Intensity of IR LEDs. By Default it set the Video type to 4: Precision Mode for tracking * //== VideoType: * //== 0 = Segment Mode * //== 1 = Grayscale Mode * //== 2 = Object Mode * //== 4 = Precision Mode * //== 6 = MJPEG Mode (V100R2 only) * \return Returns true if all cameras were set up correctly * @throw mitk::IGTException Throws an exception if System is not Initialized */ bool SetCameraParams(int exposure, int threshold, int intensity, int videoType = 4); /** * \brief Initialize the Optitrack System * \return Returns true if system was initialized correctly * @throw mitk::IGTException Throws an exception if the Optitrack Tracking System cannot be initialized */ bool InitializeCameras(); /** * \brief Add a new tool using a text file which described the tool. * The file must to have the next structure * ToolName * \#NumberOfMarkers * X Y Z - for the first marker * X Y Z - for the second marker * ... * X Y Z - for the last marker, the number \#NumberOfMarkers * X Y Z - for the PIVOT point * \return Returns true if system was initialized correctly * @throw mitk::IGTException Throws an exception if Tool could not be added or System is not Initialized */ bool AddToolByDefinitionFile(std::string fileName); // ^????? We should give an example of defined tool /** * \brief This function load a file with Tools definitions provided for the software * \return Returns true if file is correctly loaded with all the tools * @throw mitk::IGTException Throws an exception if there is an error during the Initialization */ // bool AddToolByConfigurationFil(std::string fileName); // For next release.... protected: /** * \brief Constructor & Destructor of the class */ OptitrackTrackingDevice(); ~OptitrackTrackingDevice() override; private: /** * \brief The calibration file path. This file is produced by TrackingTools/Motive software. * This variable is used to determine what will be the calibration file absolute path. */ std::string m_calibrationPath; /** * \brief The Cameras Exposition */ int m_Exp; /** * \brief The Cameras LED power */ int m_Led; /** * \brief The Cameras Thr */ int m_Thr; /** * \brief Described if the system was initialized at least once during execution. This is due * to some reported problems during the clean up , shutdown and initialization again. */ bool m_initialized; /** * \brief Vector of pointers pointing to all defined tools */ std::vector m_AllTools; /** * \brief Mutex for coordinated access of tool container */ - itk::FastMutexLock::Pointer m_ToolsMutex; - - /** - * \brief MultiThreader that starts continuous tracking update - */ - itk::MultiThreader::Pointer m_MultiThreader; + mutable std::mutex m_ToolsMutex; - /** - * \brief ThreadID number identification - */ - int m_ThreadID; + std::thread m_Thread; /* TODO: // For Tracking -bool AddToolByConfigurationFil(std::string fileName); TTAPI NPRESULT TT_LoadTrackables (const char *filename); //== Load Trackables ======---- TTAPI NPRESULT TT_SaveTrackables (const char *filename); //== Save Trackables ======---- TTAPI NPRESULT TT_AddTrackables (const char *filename); //== Add Trackables ======---- TTAPI void TT_ClearTrackableList(); //== Clear all trackables =====--- TTAPI NPRESULT TT_RemoveTrackable(int Index); //== Remove single trackable ====--- TTAPI void TT_SetTrackableEnabled(int index, bool enabled); //== Set Tracking ====--- TTAPI bool TT_TrackableEnabled(int index); //== Get Tracking ====--- TTAPI int TT_TrackableMarkerCount(int index); //== Get marker count ====--- TTAPI void TT_TrackableMarker(int RigidIndex, //== Get Trackable mrkr ====--- int MarkerIndex, float *x, float *y, float *z); //For projects TTAPI NPRESULT TT_LoadProject(const char *filename); //== Load Project File ==========-- TTAPI NPRESULT TT_SaveProject(const char *filename); //== Save Project File ==========-- // For VRPN connection TTAPI NPRESULT TT_StreamVRPN(bool enabled, int port);//== Start/stop VRPN Stream ===---- // For frame testing TTAPI int TT_FrameMarkerCount(); //== Returns Frame Markers Count --- TTAPI float TT_FrameMarkerX(int index); //== Returns X Coord of Marker ----- TTAPI float TT_FrameMarkerY(int index); //== Returns Y Coord of Marker ----- TTAPI float TT_FrameMarkerZ(int index); //== Returns Z Coord of Marker ----- TTAPI int TT_FrameMarkerLabel(int index); //== Returns Label of Marker ------- TTAPI double TT_FrameTimeStamp(); //== Time Stamp of Frame (seconds) - // For cameras handling TTAPI int TT_CameraCount(); //== Returns Camera Count =====----- TTAPI float TT_CameraXLocation(int index); //== Returns Camera's X Coord =----- TTAPI float TT_CameraYLocation(int index); //== Returns Camera's Y Coord =----- TTAPI float TT_CameraZLocation(int index); //== Returns Camera's Z Coord =----- TTAPI float TT_CameraOrientationMatrix(int camera, int index); //== Orientation ----- */ }; } #endif diff --git a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingTool.h b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingTool.h index dce6567be1..4a2f45c2ca 100644 --- a/Modules/IGT/TrackingDevices/mitkOptitrackTrackingTool.h +++ b/Modules/IGT/TrackingDevices/mitkOptitrackTrackingTool.h @@ -1,217 +1,213 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef OptiTrackTrackingTool_H_HEADER_INCLUDED_ #define OptiTrackTrackingTool_H_HEADER_INCLUDED_ #include -#include -#include "itkFastMutexLock.h" #include "mitkTrackingDevice.h" #include "mitkTrackingTool.h" #include "mitkIGTTimeStamp.h" #include #include #include #include -#include #include "mitkCommon.h" #include #include -#include #include "mitkIGTException.h" /** * \brief Function to get the Error messages from API */ #include namespace mitk { //class OptitrackTrackingDevice; /** Documentation: * \brief An object of this class represents the a Tool tracked by Optitrack System. You can define * the tool by the a definition file like in the example in ****. Remember that it will be necessary to * to have a license for using the Optitrack System. * See http://www.naturalpoint.com/ for details. * \author E. Marinetto (emarinetto@hggm.es) Instituto de Investigación Sanitaria Gregorio Marañón, Madrid, Spain. & M. Noll (matthias.noll@igd.fraunhofer.de) Cognitive Computing & Medical Imaging | Fraunhofer IGD * \ingroup IGT */ class MITKIGT_EXPORT OptitrackTrackingTool : public TrackingTool { public: friend class OptitrackTrackingDevice; mitkClassMacro(mitk::OptitrackTrackingTool, mitk::TrackingTool); itkNewMacro(Self); /** * \brief Define the tool by a calibration File. * The file must to have the next structure. Makers locations must to have "%fe %fe %fe\n" format and in (mm). See http://www.cplusplus.com/reference/cstdio/fscanf/ * ToolName * \#NumberOfMarkers * X Y Z - for the first marker * X Y Z - for the second marker * ... * X Y Z - for the last marker, the number \#NumberOfMarkers * X Y Z - for the PIVOT point * \return Returns true if the tool was set correctly * @throw mitk::IGTException Throws an exception if there exist any problem during the configuration file reading. */ bool SetToolByFileName(std::string nameFile); /** * \brief Ask API the next number of defined tool * \return Returns the next ID (int) for a new tool in the device list for API * @throw mitk::IGTException Throws an exception if get_IDnext failed */ int get_IDnext(); /** * \brief Delete the tool from the list of tools inside API Optitrack * \return Returns true if the deletion was correct * @throw mitk::IGTException Throws an exception if */ bool DeleteTrackable(); /** * \brief Set the position to a given one * @throw mitk::IGTException Throws an exception if */ using Superclass::SetPosition; void SetPosition(mitk::Point3D position, ScalarType eps=0.0); /** * \brief Set the orientation to a given one using a quaternion nomenclature * @throw mitk::IGTException Throws an exception if */ using Superclass::SetOrientation; void SetOrientation(mitk::Quaternion orientation, ScalarType eps=0.0); /** * \brief Get the position of the tool * @throw mitk::IGTException Throws an exception if */ void GetPosition(mitk::Point3D& position) const override; /** * \brief Get the orientation of the tool using quaternion nomenclature * @throw mitk::IGTException Throws an exception if */ void GetOrientation(mitk::Quaternion& orientation) const override; /** * \brief Set the tool enabled for tracking. * \return Return true if the enabling was successfull * @throw mitk::IGTException Throws an exception if */ bool Enable() override; /** * \brief Set the tool disabled for tracking. * \return Return true if the disabling was successfull * @throw mitk::IGTException Throws an exception if */ bool Disable() override; /** * \brief Check if the tool is enabled (true) or not. * \return Return true if the tool is enabled for tracking * @throw mitk::IGTException Throws an exception if */ bool IsEnabled() const override; /** * \brief Check if the data of the tool is valid. * \return Return true if location data is valid * @throw mitk::IGTException Throws an exception if */ bool IsDataValid() const override; /** * \brief Get the expectated error in the tracked tool * \return Return the error location * @throw mitk::IGTException Throws an exception if */ float GetTrackingError() const override; /** * \brief Set the FLE (Fiducial Localization Error) for the tool * @throw mitk::IGTException Throws an exception if */ void SetTrackingError(float FLEerror) override; /** * \brief Set the valid flag for tracking data to true * @throw mitk::IGTException Throws an exception if */ void SetDataValid(bool _arg) override; /** * \brief Update location and orientation of the tool * @throw mitk::IGTException Throws an exception if */ void updateTool(); /** * \brief Constructor of the class */ OptitrackTrackingTool(); /** * \brief Destructor of the class */ ~OptitrackTrackingTool() override; /** * \brief File of the configuration for the tool */ std::string m_fileConfiguration; /** * \brief ID number from Optitrack API */ int m_ID; /** * \brief List of Markers locations in calibration position and orientation */ float* m_calibrationPoints; /** * \brief location of the pivot point during calibration */ float* m_pivotPoint; /** * \brief Number of Markers that blong to the tool */ int m_numMarkers; /** * \brief Expected value of the fiducial localization error (rms) */ float m_FLE; private: OptitrackTrackingTool(const OptitrackTrackingTool&); const OptitrackTrackingTool& operator=(const OptitrackTrackingTool&); }; } #endif /* OptiTrackTrackingTool_H_HEADER_INCLUDED_ */ diff --git a/Modules/IGT/TrackingDevices/mitkTrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkTrackingDevice.cpp index f468dd7389..7c21f405e7 100644 --- a/Modules/IGT/TrackingDevices/mitkTrackingDevice.cpp +++ b/Modules/IGT/TrackingDevices/mitkTrackingDevice.cpp @@ -1,153 +1,146 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkTrackingDevice.h" #include "mitkIGTTimeStamp.h" #include "mitkTrackingTool.h" -#include - #include #include #include "mitkUnspecifiedTrackingTypeInformation.h" #include "mitkTrackingDeviceTypeCollection.h" -typedef itk::MutexLockHolder MutexLockHolder; - mitk::TrackingDevice::TrackingDevice() : m_State(mitk::TrackingDevice::Setup), m_Data(mitk::UnspecifiedTrackingTypeInformation::GetDeviceDataUnspecified()), m_StopTracking(false), m_RotationMode(mitk::TrackingDevice::RotationStandard) { - m_StopTrackingMutex = itk::FastMutexLock::New(); - m_StateMutex = itk::FastMutexLock::New(); - m_TrackingFinishedMutex = itk::FastMutexLock::New(); } mitk::TrackingDevice::~TrackingDevice() { } bool mitk::TrackingDevice::IsDeviceInstalled() { return true; //this is the default for all tracking device //If a device needs installation please reimplement //this method in the subclass. } bool mitk::TrackingDevice::AutoDetectToolsAvailable() { return false; } bool mitk::TrackingDevice::AddSingleToolIsAvailable() { return true; } mitk::NavigationToolStorage::Pointer mitk::TrackingDevice::AutoDetectTools() { return mitk::NavigationToolStorage::New(); } mitk::TrackingDevice::TrackingDeviceState mitk::TrackingDevice::GetState() const { - MutexLockHolder lock(*m_StateMutex); + std::lock_guard lock(m_StateMutex); return m_State; } void mitk::TrackingDevice::SetState( TrackingDeviceState state ) { itkDebugMacro("setting m_State to " << state); - MutexLockHolder lock(*m_StateMutex); // lock and unlock the mutex + std::lock_guard lock(m_StateMutex); if (m_State == state) { return; } m_State = state; this->Modified(); } void mitk::TrackingDevice::SetRotationMode(RotationMode) { MITK_WARN << "Rotation mode switching is not implemented for this device. Leaving it at mitk::TrackingDevice::RotationStandard"; } mitk::TrackingDeviceType mitk::TrackingDevice::GetType() const{ return m_Data.Line; } void mitk::TrackingDevice::SetType(mitk::TrackingDeviceType deviceType){ us::ModuleContext* context = us::GetModuleContext(); std::vector > refs = context->GetServiceReferences(); if (refs.empty()) { MITK_ERROR << "No tracking device service found!"; } mitk::TrackingDeviceTypeCollection* deviceTypeCollection = context->GetService(refs.front()); m_Data = deviceTypeCollection->GetFirstCompatibleDeviceDataForLine(deviceType); } mitk::TrackingDeviceData mitk::TrackingDevice::GetData() const{ return m_Data; } void mitk::TrackingDevice::SetData(mitk::TrackingDeviceData data){ m_Data = data; } bool mitk::TrackingDevice::StopTracking() { if (this->GetState() == Tracking) // Only if the object is in the correct state { - m_StopTrackingMutex->Lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling + m_StopTrackingMutex.lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling m_StopTracking = true; - m_StopTrackingMutex->Unlock(); + m_StopTrackingMutex.unlock(); //we have to wait here that the other thread recognizes the STOP-command and executes it - m_TrackingFinishedMutex->Lock(); + m_TrackingFinishedMutex.lock(); mitk::IGTTimeStamp::GetInstance()->Stop(this); // notify realtime clock // StopTracking was called, thus the mode should be changed back // to Ready now that the tracking loop has ended. this->SetState(Ready); - m_TrackingFinishedMutex->Unlock(); + m_TrackingFinishedMutex.unlock(); } return true; } mitk::TrackingTool* mitk::TrackingDevice::GetToolByName( std::string name ) const { unsigned int toolCount = this->GetToolCount(); for (unsigned int i = 0; i < toolCount; ++i) if (name == this->GetTool(i)->GetToolName()) return this->GetTool(i); return nullptr; } std::string mitk::TrackingDevice::GetTrackingDeviceName() { return this->GetData().Line; } diff --git a/Modules/IGT/TrackingDevices/mitkTrackingDevice.h b/Modules/IGT/TrackingDevices/mitkTrackingDevice.h index d7c49f67bd..4345c50af7 100644 --- a/Modules/IGT/TrackingDevices/mitkTrackingDevice.h +++ b/Modules/IGT/TrackingDevices/mitkTrackingDevice.h @@ -1,205 +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. ============================================================================*/ #ifndef MITKTRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2 #define MITKTRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2 #include #include "itkObject.h" #include "mitkCommon.h" #include "mitkTrackingTypes.h" -#include "itkFastMutexLock.h" #include "mitkNavigationToolStorage.h" - +#include namespace mitk { class TrackingTool; // interface for a tool that can be tracked by the TrackingDevice /**Documentation * \brief Interface for all Tracking Devices * * Defines the methods that are common for all tracking devices. * * \ingroup IGT */ class MITKIGT_EXPORT TrackingDevice : public itk::Object { public: mitkClassMacroItkParent(TrackingDevice, itk::Object); /** Defines the rotation modes of this tracking device which results in different representations * of quaternions. * * - Standard: normal representation, rawdata from the device is not changed (DEFAULT) * * - Transposed: the rotation is stored transposed, which is (by mistake!) expected by some older MITK classes due * to an ambigious method naming in VNL. * * CAUTION: The rotation mode can only be changed for backward compatibility of old WRONG code. * PLEASE DO NOT CHANGE THE ROTATION MODE UNLESS YOU ARE KNOWING EXACTLY WHAT YOU ARE DOING! * * use SetRotationMode to change the mode. */ enum RotationMode {RotationStandard, RotationTransposed}; enum TrackingDeviceState {Setup, Ready, Tracking}; ///< Type for state variable. The trackingdevice is always in one of these states /** * \brief Opens a connection to the device * * This may only be called if there is currently no connection to the device. * If OpenConnection() is successful, the object will change from Setup state to Ready state */ virtual bool OpenConnection() = 0; /** * \brief Closes the connection to the device * * This may only be called if there is currently a connection to the device, but tracking is * not running (e.g. object is in Ready state) */ virtual bool CloseConnection() = 0; ///< Closes the connection with the device /** * \brief start retrieving tracking data from the device. * * This may only be called after the connection to the device has been established * with a call to OpenConnection() (E.g. object is in Ready mode). This will change the * object state from Ready to Tracking */ virtual bool StartTracking() = 0; /** * \brief stop retrieving tracking data from the device. * stop retrieving tracking data from the device. * This may only be called after StartTracking was called * (e.g. the object is in Tracking mode). * This will change the object state from Tracking to Ready. */ virtual bool StopTracking(); /** * \brief Return tool with index toolNumber * * tools are numbered from 0 to GetToolCount() - 1. */ virtual TrackingTool* GetTool(unsigned int toolNumber) const = 0; /** * \brief Returns the tool with the given tool name * * Note: subclasses can and should implement optimized versions of this method * \return the given tool or nullptr if no tool with that name exists */ virtual mitk::TrackingTool* GetToolByName(std::string name) const; /** * \brief Returns number of tracking tools */ virtual unsigned int GetToolCount() const = 0; /** Sets the rotation mode of this class. See documentation of enum RotationMode for details * on the different modes. This method has to be implemented in a deriving class to become * functional / if different rotation modes should be supported. * CAUTION: The rotation mode can only be changed for backward compatibility of old WRONG code. * PLEASE DO NOT CHANGE THE ROTATION MODE UNLESS YOU ARE KNOWING EXACTLY WHAT YOU ARE DOING! */ virtual void SetRotationMode(RotationMode r); /** @return Returns the rotation mode of this class. See documentation of enum * RotationMode for details on the different modes. */ itkGetConstMacro(RotationMode,RotationMode); /** * \brief return current object state (Setup, Ready or Tracking) */ TrackingDeviceState GetState() const; /** * \brief Deprecated! Use the more specific getData or GetTrackingDeviceName instead. return device type identifier */ TrackingDeviceType GetType() const; /** * \brief Deprecated! Use the more specific setDeviceData instead. set device type */ void SetType(TrackingDeviceType type); /** * \brief Convenient Method to get the Name of the Tracking Device. * This is identical with GetData().Line and can be used to compare with TrackingDeviceTypeInformation::GetTrackingDeviceName() * to check if you have a specific device. */ std::string GetTrackingDeviceName(); /** * \brief return device data */ TrackingDeviceData GetData() const; /** * \brief set device type */ void SetData(TrackingDeviceData data); /** * @return Returns true if the device is installed on this system an can be used. * Installed means activated in MITK, in some cases this means the MITK * installation / build has to know the installation path of the device * libraries on this system. This path is usually given as cmake variable * during the build configuration in devellopers mode. If the device should * be available for end users with an installer the libraries can be included * into the installer or the installer has to be adapted such that it asks * for the path. * Returns fals if the device is not installed. It cannot be used on this build * in this case. * * Note that some tracking systems communicate via a standard interface (e.g., serial * port) and don't need any library or installation. These devices are always "installed". */ virtual bool IsDeviceInstalled(); /** @return Returns true if this device can autodetects its tools. */ virtual bool AutoDetectToolsAvailable(); /** @return Returns true if it is possible to add a single tool. Default return is true.*/ virtual bool AddSingleToolIsAvailable(); /** Autodetects tools from this device and returns them as a navigation tool storage. * @return Returns the detected tools. Returns an empty storage if no tools are present * or if detection is not possible */ virtual mitk::NavigationToolStorage::Pointer AutoDetectTools(); private: TrackingDeviceState m_State; ///< current object state (Setup, Ready or Tracking) protected: /** * \brief change object state */ void SetState(TrackingDeviceState state); TrackingDevice(); ~TrackingDevice() override; TrackingDeviceData m_Data; ///< current device Data bool m_StopTracking; ///< signal stop to tracking thread - itk::FastMutexLock::Pointer m_StopTrackingMutex; ///< mutex to control access to m_StopTracking - itk::FastMutexLock::Pointer m_TrackingFinishedMutex; ///< mutex to manage control flow of StopTracking() - itk::FastMutexLock::Pointer m_StateMutex; ///< mutex to control access to m_State + std::mutex m_StopTrackingMutex; ///< mutex to control access to m_StopTracking + std::mutex m_TrackingFinishedMutex; ///< mutex to manage control flow of StopTracking() + mutable std::mutex m_StateMutex; ///< mutex to control access to m_State RotationMode m_RotationMode; ///< defines the rotation mode Standard or Transposed, Standard is default }; } // namespace mitk #endif /* MITKTRACKINGDEVICE_H_HEADER_INCLUDED_C1C2FCD2 */ diff --git a/Modules/IGT/TrackingDevices/mitkTrackingTool.cpp b/Modules/IGT/TrackingDevices/mitkTrackingTool.cpp index 76ab4f6998..15b30743b3 100644 --- a/Modules/IGT/TrackingDevices/mitkTrackingTool.cpp +++ b/Modules/IGT/TrackingDevices/mitkTrackingTool.cpp @@ -1,289 +1,283 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkTrackingTool.h" -#include - -typedef itk::MutexLockHolder MutexLockHolder; mitk::TrackingTool::TrackingTool() : itk::Object(), m_ToolName(""), m_ErrorMessage(""), m_IGTTimeStamp(0), - m_MyMutex(itk::FastMutexLock::New()), m_TrackingError(0.0f), m_Enabled(true), m_DataValid(false), m_ToolTipSet(false) { m_Position[0] = 0.0f; m_Position[1] = 0.0f; m_Position[2] = 0.0f; m_Orientation[0] = 0.0f; m_Orientation[1] = 0.0f; m_Orientation[2] = 0.0f; m_Orientation[3] = 0.0f; // this should not be necessary as the tools bring their own tooltip transformation m_ToolTipPosition[0] = 0.0f; m_ToolTipPosition[1] = 0.0f; m_ToolTipPosition[2] = 0.0f; m_ToolAxisOrientation[0] = 0.0f; m_ToolAxisOrientation[1] = 0.0f; m_ToolAxisOrientation[2] = 0.0f; m_ToolAxisOrientation[3] = 1.0f; } mitk::TrackingTool::~TrackingTool() { - m_MyMutex->Unlock(); - m_MyMutex = nullptr; } void mitk::TrackingTool::PrintSelf(std::ostream& os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "ToolName: " << m_ToolName << std::endl; os << indent << "ErrorMesage: " << m_ErrorMessage << std::endl; os << indent << "Position: " << m_Position << std::endl; os << indent << "Orientation: " << m_Orientation << std::endl; os << indent << "TrackingError: " << m_TrackingError << std::endl; os << indent << "Enabled: " << m_Enabled << std::endl; os << indent << "DataValid: " << m_DataValid << std::endl; os << indent << "ToolTip: " << m_ToolTipPosition << std::endl; os << indent << "ToolTipRotation: " << m_ToolAxisOrientation << std::endl; os << indent << "ToolTipSet: " << m_ToolTipSet << std::endl; } const char* mitk::TrackingTool::GetToolName() const { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); return this->m_ToolName.c_str(); } void mitk::TrackingTool::SetToolName(const char* _arg) { itkDebugMacro("setting m_ToolName to " << _arg); - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); if ( _arg && (_arg == this->m_ToolName) ) { return; } if (_arg) { this->m_ToolName= _arg; } else { this->m_ToolName= ""; } this->Modified(); } void mitk::TrackingTool::SetToolName( const std::string _arg ) { this->SetToolName(_arg.c_str()); } mitk::Point3D mitk::TrackingTool::GetToolTipPosition() const { - MutexLockHolder lock(*m_MyMutex); + std::lock_guard lock(m_MyMutex); return m_ToolTipPosition; } mitk::Quaternion mitk::TrackingTool::GetToolAxisOrientation() const { - MutexLockHolder lock(*m_MyMutex); + std::lock_guard lock(m_MyMutex); return m_ToolAxisOrientation; } void mitk::TrackingTool::SetToolTipPosition(mitk::Point3D toolTipPosition, mitk::Quaternion orientation, mitk::ScalarType eps) { if ( !Equal(m_ToolTipPosition, toolTipPosition, eps) || !Equal(m_ToolAxisOrientation, orientation, eps) ) { if( (toolTipPosition[0] == 0) && (toolTipPosition[1] == 0) && (toolTipPosition[2] == 0) && (orientation.x() == 0) && (orientation.y() == 0) && (orientation.z() == 0) && (orientation.r() == 1)) { m_ToolTipSet = false; } else { m_ToolTipSet = true; } m_ToolTipPosition = toolTipPosition; m_ToolAxisOrientation = orientation; this->Modified(); } } bool mitk::TrackingTool::IsToolTipSet() const { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); return m_ToolTipSet; } void mitk::TrackingTool::GetPosition(mitk::Point3D& position) const { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); if (m_ToolTipSet) { // Compute the position of tool tip in the coordinate frame of the // tracking device: Rotate the position of the tip into the tracking // device coordinate frame then add to the position of the tracking // sensor vnl_vector pos_vnl = m_Position.GetVnlVector() + m_Orientation.rotate( m_ToolTipPosition.GetVnlVector() ) ; position[0] = pos_vnl[0]; position[1] = pos_vnl[1]; position[2] = pos_vnl[2]; } else { position[0] = m_Position[0]; position[1] = m_Position[1]; position[2] = m_Position[2]; } this->Modified(); } void mitk::TrackingTool::SetPosition(mitk::Point3D position) { itkDebugMacro("setting m_Position to " << position); - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); if (m_Position != position) { m_Position = position; this->Modified(); } } void mitk::TrackingTool::GetOrientation(mitk::Quaternion& orientation) const { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); if (m_ToolTipSet) { // Compute the orientation of the tool tip in the coordinate frame of // the tracking device. // // * m_Orientation is the orientation of the sensor relative to the transmitter // * m_ToolAxisOrientation is the orientation of the tool tip relative to the sensor orientation = m_Orientation * m_ToolAxisOrientation; } else { orientation = m_Orientation; } } void mitk::TrackingTool::SetOrientation(mitk::Quaternion orientation) { itkDebugMacro("setting m_Orientation to " << orientation); - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); if (m_Orientation != orientation) { m_Orientation = orientation; this->Modified(); } } bool mitk::TrackingTool::Enable() { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); if (m_Enabled == false) { this->m_Enabled = true; this->Modified(); } return true; } bool mitk::TrackingTool::Disable() { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); if (m_Enabled == true) { this->m_Enabled = false; this->Modified(); } return true; } bool mitk::TrackingTool::IsEnabled() const { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); return m_Enabled; } void mitk::TrackingTool::SetDataValid(bool isDataValid) { itkDebugMacro("setting m_DataValid to " << isDataValid); if (this->m_DataValid != isDataValid) { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); this->m_DataValid = isDataValid; this->Modified(); } } bool mitk::TrackingTool::IsDataValid() const { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); return m_DataValid; } float mitk::TrackingTool::GetTrackingError() const { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); return m_TrackingError; } void mitk::TrackingTool::SetTrackingError(float error) { itkDebugMacro("setting m_TrackingError to " << error); - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); if (m_TrackingError != error) { m_TrackingError = error; this->Modified(); } } const char* mitk::TrackingTool::GetErrorMessage() const { - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); return this->m_ErrorMessage.c_str(); } void mitk::TrackingTool::SetErrorMessage(const char* _arg) { itkDebugMacro("setting m_ErrorMessage to " << _arg); - MutexLockHolder lock(*m_MyMutex); // lock and unlock the mutex + std::lock_guard lock(m_MyMutex); if ((_arg == nullptr) || (_arg == this->m_ErrorMessage)) return; if (_arg != nullptr) this->m_ErrorMessage = _arg; else this->m_ErrorMessage = ""; this->Modified(); } diff --git a/Modules/IGT/TrackingDevices/mitkTrackingTool.h b/Modules/IGT/TrackingDevices/mitkTrackingTool.h index f33b54b249..dd02f3ac9d 100644 --- a/Modules/IGT/TrackingDevices/mitkTrackingTool.h +++ b/Modules/IGT/TrackingDevices/mitkTrackingTool.h @@ -1,101 +1,101 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKTRACKINGTOOL_H_HEADER_INCLUDED_ #define MITKTRACKINGTOOL_H_HEADER_INCLUDED_ #include #include #include #include -#include +#include namespace mitk { /**Documentation * \brief Interface for all Tracking Tools * * This class is a complete TrackingTool implementation. It can either be used directly by * TrackingDevices, or be subclassed for more specific implementations. * mitk::MicroBirdTrackingDevice uses this class to manage its tools. Other tracking devices * uses specialized versions of this class (e.g. mitk::NDITrackingTool) * * The TrackingTool class holds all coordinate transforms associated with tracking of a tool. * The sensor attached to the tool is localized in the global tracking coordinate system (m_Position, m_Orientation). * A tool tip (m_ToolTipPosition) can be defined in sensor coordinates. * The tool axis defines the main axis of the tool and is defined as the negative z-axis of the tool tip coordinate system * The main axis of the representation object of the tool (e.g. a surface) has to be defined along the negative z-axis * \imageMacro{TrackingTool.png,"Coordinate transforms associated to the tracking tool.",3} * * \ingroup IGT */ class MITKIGT_EXPORT TrackingTool : public itk::Object { public: mitkClassMacroItkParent(TrackingTool, itk::Object); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void PrintSelf(std::ostream& os, itk::Indent indent) const override; virtual const char* GetToolName() const; ///< every tool has a name thatgit can be used to identify it. virtual void SetToolName(const std::string _arg); ///< Sets the name of the tool virtual void SetToolName(const char* _arg); ///< Sets the name of the tool Point3D GetToolTipPosition() const; ///< returns the tool tip in tool coordinates, which where set by SetToolTip Quaternion GetToolAxisOrientation() const; ///< returns the transformation of the tool axis with respect to the MITK-IGT main tool axis (0,0,-1) virtual void SetToolTipPosition(Point3D toolTipPosition, Quaternion orientation, ScalarType eps=0.0); ///< defines a tool tip for this tool in tool coordinates. GetPosition() and GetOrientation() return the data of the tool tip if it is defined. By default no tooltip is defined. virtual bool IsToolTipSet() const; ///< returns true if a tool tip is set, false if not virtual void GetPosition(Point3D& position) const; ///< returns the current position of the tool as an array of three floats (in the tracking device coordinate system) virtual void SetPosition(Point3D position); ///< sets the position virtual void GetOrientation(Quaternion& orientation) const; ///< returns the current orientation of the tool as a quaternion in a mitk::Point4D (in the tracking device coordinate system) virtual void SetOrientation(Quaternion orientation); ///< sets the orientation as a quaternion virtual bool Enable(); ///< enables the tool, so that it will be tracked virtual bool Disable(); ///< disables the tool, so that it will not be tracked anymore virtual bool IsEnabled() const; ///< returns whether the tool is enabled or disabled virtual void SetDataValid(bool isDataValid); ///< sets if the tracking data (position & orientation) is valid virtual bool IsDataValid() const; ///< returns true if the current position data is valid (no error during tracking, tracking error below threshold, ...) virtual float GetTrackingError() const; ///< returns one value that corresponds to the overall tracking error. virtual void SetTrackingError(float error); ///< sets the tracking error virtual const char* GetErrorMessage() const; ///< if the data is not valid, ErrorMessage should contain a string explaining why it is invalid (the Set-method should be implemented in subclasses, it should not be accessible by the user) virtual void SetErrorMessage(const char* _arg); ///< sets the error message itkSetMacro(IGTTimeStamp, double) ///< Sets the IGT timestamp of the tracking tool object (time in milliseconds) itkGetConstMacro(IGTTimeStamp, double) ///< Gets the IGT timestamp of the tracking tool object (time in milliseconds). Returns 0 if the timestamp was not set. protected: TrackingTool(); ~TrackingTool() override; std::string m_ToolName; ///< every tool has a name that can be used to identify it. std::string m_ErrorMessage; ///< if a tool is invalid, this member should contain a human readable explanation of why it is invalid double m_IGTTimeStamp; ///< contains the time at which the tracking data was recorded - itk::FastMutexLock::Pointer m_MyMutex; ///< mutex to control concurrent access to the tool + mutable std::mutex m_MyMutex; ///< mutex to control concurrent access to the tool Point3D m_Position; ///< holds the position of the tool in global tracking coordinates Quaternion m_Orientation; ///< holds the orientation of the tool´in global tracking coordinates float m_TrackingError; ///< holds the tracking error of the tool bool m_Enabled; ///< if true, tool is enabled and should receive tracking updates from the tracking device bool m_DataValid; ///< if true, data in m_Position and m_Orientation is valid, e.g. true tracking data Point3D m_ToolTipPosition; ///< holds the position of the tool tip in the coordinate system of the tracking sensor Quaternion m_ToolAxisOrientation; ///< holds the rotation of the sensor coordinate system such that the z-axis coincides with the main tool axis e.g. obtained by a tool calibration bool m_ToolTipSet; }; } // namespace mitk #endif /* MITKTRACKINGTOOL_H_HEADER_INCLUDED_ */ diff --git a/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.cpp index 51fbec3ea0..cdf8a4a56f 100644 --- a/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.cpp +++ b/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.cpp @@ -1,331 +1,307 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkVirtualTrackingDevice.h" #include "mitkIGTTimeStamp.h" #include "mitkIGTException.h" #include #include #include #include -#include #include #include -typedef itk::MutexLockHolder MutexLockHolder; - mitk::VirtualTrackingDevice::VirtualTrackingDevice() : mitk::TrackingDevice(), -m_AllTools(), m_ToolsMutex(nullptr), m_MultiThreader(nullptr), m_ThreadID(-1), m_RefreshRate(100), m_NumberOfControlPoints(20), m_GaussianNoiseEnabled(false), +m_AllTools(), m_RefreshRate(100), m_NumberOfControlPoints(20), m_GaussianNoiseEnabled(false), m_MeanDistributionParam(0.0), m_DeviationDistributionParam(1.0) { m_Data = mitk::VirtualTrackerTypeInformation::GetDeviceDataVirtualTracker(); m_Bounds[0] = m_Bounds[2] = m_Bounds[4] = -400.0; // initialize bounds to -400 ... +400 (mm) cube m_Bounds[1] = m_Bounds[3] = m_Bounds[5] = 400.0; - m_ToolsMutex = itk::FastMutexLock::New(); } mitk::VirtualTrackingDevice::~VirtualTrackingDevice() { if (this->GetState() == Tracking) { this->StopTracking(); } if (this->GetState() == Ready) { this->CloseConnection(); } /* cleanup tracking thread */ - if (m_MultiThreader.IsNotNull() && (m_ThreadID != -1)) - { - m_MultiThreader->TerminateThread(m_ThreadID); - m_MultiThreader = nullptr; - } + if (m_Thread.joinable()) + m_Thread.join(); + m_AllTools.clear(); } mitk::TrackingTool* mitk::VirtualTrackingDevice::AddTool(const char* toolName) { //if (this->GetState() == Tracking) //{ // return nullptr; //} mitk::VirtualTrackingTool::Pointer t = mitk::VirtualTrackingTool::New(); t->SetToolName(toolName); t->SetVelocity(0.1); this->InitializeSpline(t); - MutexLockHolder lock(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard lock(m_ToolsMutex); // lock and unlock the mutex m_AllTools.push_back(t); return t; } bool mitk::VirtualTrackingDevice::StartTracking() { if (this->GetState() != Ready) return false; this->SetState(Tracking); // go to mode Tracking - this->m_StopTrackingMutex->Lock(); + this->m_StopTrackingMutex.lock(); this->m_StopTracking = false; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); mitk::IGTTimeStamp::GetInstance()->Start(this); - if (m_MultiThreader.IsNotNull() && (m_ThreadID != -1)) - m_MultiThreader->TerminateThread(m_ThreadID); - if (m_MultiThreader.IsNull()) - m_MultiThreader = itk::MultiThreader::New(); + if (m_Thread.joinable()) + m_Thread.detach(); - m_ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); // start a new thread that executes the TrackTools() method + m_Thread = std::thread(&VirtualTrackingDevice::ThreadStartTracking, this); // start a new thread that executes the TrackTools() method return true; } bool mitk::VirtualTrackingDevice::StopTracking() { if (this->GetState() == Tracking) // Only if the object is in the correct state { - m_StopTrackingMutex->Lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling + m_StopTrackingMutex.lock(); // m_StopTracking is used by two threads, so we have to ensure correct thread handling m_StopTracking = true; - m_StopTrackingMutex->Unlock(); + m_StopTrackingMutex.unlock(); - m_TrackingFinishedMutex->Lock(); + m_TrackingFinishedMutex.lock(); this->SetState(Ready); - m_TrackingFinishedMutex->Unlock(); + m_TrackingFinishedMutex.unlock(); } mitk::IGTTimeStamp::GetInstance()->Stop(this); return true; } unsigned int mitk::VirtualTrackingDevice::GetToolCount() const { - MutexLockHolder lock(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard lock(m_ToolsMutex); // lock and unlock the mutex return static_cast(this->m_AllTools.size()); } mitk::TrackingTool* mitk::VirtualTrackingDevice::GetTool(unsigned int toolNumber) const { - MutexLockHolder lock(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard lock(m_ToolsMutex); // lock and unlock the mutex if (toolNumber < m_AllTools.size()) return this->m_AllTools.at(toolNumber); return nullptr; } bool mitk::VirtualTrackingDevice::OpenConnection() { if (m_NumberOfControlPoints < 1) { mitkThrowException(mitk::IGTException) << "to few control points for spline interpolation"; } srand(time(nullptr)); //Init random number generator this->SetState(Ready); return true; } void mitk::VirtualTrackingDevice::InitializeSpline(mitk::VirtualTrackingTool* t) { if (t == nullptr) return; typedef mitk::VirtualTrackingTool::SplineType SplineType; /* create random control points */ SplineType::ControlPointListType controlPoints; controlPoints.reserve(m_NumberOfControlPoints + 1); controlPoints.push_back(this->GetRandomPoint()); // insert point 0 double length = 0.0; // estimate spline length by calculating line segments lengths for (unsigned int i = 1; i < m_NumberOfControlPoints - 1; ++i) // set points 1..n-2 { SplineType::ControlPointType pos; pos = this->GetRandomPoint(); length += controlPoints.at(i - 1).EuclideanDistanceTo(pos); controlPoints.push_back(pos); } controlPoints.push_back(controlPoints.at(0)); // close spline --> insert point last control point with same value as first control point length += controlPoints.at(controlPoints.size() - 2).EuclideanDistanceTo(controlPoints.at(controlPoints.size() - 1)); /* Create knot list. TODO: rethink knot list values and list size. Is there a better solution? */ SplineType::KnotListType knotList; knotList.push_back(0.0); for (unsigned int i = 1; i < controlPoints.size() + t->GetSpline()->GetSplineOrder() + 1; ++i) knotList.push_back(i); knotList.push_back(controlPoints.size() + t->GetSpline()->GetSplineOrder() + 1); t->GetSpline()->SetControlPoints(controlPoints); t->GetSpline()->SetKnots(knotList); t->SetSplineLength(length); } bool mitk::VirtualTrackingDevice::CloseConnection() { bool returnValue = true; if (this->GetState() == Setup) return true; this->SetState(Setup); return returnValue; } mitk::ScalarType mitk::VirtualTrackingDevice::GetSplineChordLength(unsigned int idx) { mitk::VirtualTrackingTool* t = this->GetInternalTool(idx); if (t != nullptr) return t->GetSplineLength(); else throw std::invalid_argument("invalid index"); } void mitk::VirtualTrackingDevice::SetToolSpeed(unsigned int idx, mitk::ScalarType roundsPerSecond) { if (roundsPerSecond < 0.0001) throw std::invalid_argument("Minimum tool speed is 0.0001 rounds per second"); mitk::VirtualTrackingTool* t = this->GetInternalTool(idx); if (t != nullptr) t->SetVelocity(roundsPerSecond); else throw std::invalid_argument("invalid index"); } mitk::VirtualTrackingTool* mitk::VirtualTrackingDevice::GetInternalTool(unsigned int idx) { - MutexLockHolder toolsMutexLockHolder(*m_ToolsMutex); // lock and unlock the mutex + std::lock_guard toolsMutexLockHolder(m_ToolsMutex); // lock and unlock the mutex if (idx < m_AllTools.size()) return m_AllTools.at(idx); else return nullptr; } void mitk::VirtualTrackingDevice::TrackTools() { /* lock the TrackingFinishedMutex to signal that the execution rights are now transfered to the tracking thread */ - MutexLockHolder trackingFinishedLockHolder(*m_TrackingFinishedMutex); // keep lock until end of scope + std::lock_guard trackingFinishedLockHolder(m_TrackingFinishedMutex); // keep lock until end of scope if (this->GetState() != Tracking) return; bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here - this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking + this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking localStopTracking = this->m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); mitk::ScalarType t = 0.0; while ((this->GetState() == Tracking) && (localStopTracking == false)) { //for (ToolContainer::iterator itAllTools = m_AllTools.begin(); itAllTools != m_AllTools.end(); itAllTools++) for (unsigned int i = 0; i < this->GetToolCount(); ++i) // use mutexed methods to access tool container { mitk::VirtualTrackingTool::Pointer currentTool = this->GetInternalTool(i); mitk::VirtualTrackingTool::SplineType::PointType pos; /* calculate tool position with spline interpolation */ pos = currentTool->GetSpline()->EvaluateSpline(t); mitk::Point3D mp; mitk::itk2vtk(pos, mp); // convert from SplineType::PointType to mitk::Point3D //Add Gaussian Noise to Tracking Coordinates if enabled if (this->m_GaussianNoiseEnabled) { std::random_device rd; std::mt19937 generator(rd()); std::normal_distribution dist(this->m_MeanDistributionParam, this->m_DeviationDistributionParam); double noise = dist(generator); mp = mp + noise; } currentTool->SetPosition(mp); // Currently, a constant speed is used. TODO: use tool velocity setting t += 0.001; if (t >= 1.0) t = 0.0; mitk::Quaternion quat; /* fix quaternion rotation */ quat.x() = 0.0; quat.y() = 0.0; quat.z() = 0.0; quat.r() = 1.0; quat.normalize(); currentTool->SetOrientation(quat); // TODO: rotate once per cycle around a fixed rotation vector currentTool->SetTrackingError(2 * (rand() / (RAND_MAX + 1.0))); // tracking error in 0 .. 2 Range currentTool->SetDataValid(true); currentTool->Modified(); } itksys::SystemTools::Delay(m_RefreshRate); /* Update the local copy of m_StopTracking */ - this->m_StopTrackingMutex->Lock(); + this->m_StopTrackingMutex.lock(); localStopTracking = m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); } // tracking ends if we pass this line } -ITK_THREAD_RETURN_TYPE mitk::VirtualTrackingDevice::ThreadStartTracking(void* pInfoStruct) +void mitk::VirtualTrackingDevice::ThreadStartTracking() { - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - if (pInfo == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - VirtualTrackingDevice *trackingDevice = static_cast(pInfo->UserData); - - if (trackingDevice != nullptr) - trackingDevice->TrackTools(); - - trackingDevice->m_ThreadID = -1; // reset thread ID because we end the thread here - return ITK_THREAD_RETURN_VALUE; + this->TrackTools(); } mitk::VirtualTrackingDevice::ControlPointType mitk::VirtualTrackingDevice::GetRandomPoint() { ControlPointType pos; pos[0] = m_Bounds[0] + (m_Bounds[1] - m_Bounds[0]) * (rand() / (RAND_MAX + 1.0)); // X = xMin + xRange * (random number between 0 and 1) pos[1] = m_Bounds[2] + (m_Bounds[3] - m_Bounds[2]) * (rand() / (RAND_MAX + 1.0)); // Y pos[2] = m_Bounds[4] + (m_Bounds[5] - m_Bounds[4]) * (rand() / (RAND_MAX + 1.0)); // Z return pos; } void mitk::VirtualTrackingDevice::EnableGaussianNoise() { this->m_GaussianNoiseEnabled = true; } void mitk::VirtualTrackingDevice::DisableGaussianNoise() { this->m_GaussianNoiseEnabled = false; } void mitk::VirtualTrackingDevice::SetParamsForGaussianNoise(double meanDistribution, double deviationDistribution) { this->m_MeanDistributionParam = meanDistribution; this->m_DeviationDistributionParam = deviationDistribution; } double mitk::VirtualTrackingDevice::GetDeviationDistribution() { return m_DeviationDistributionParam; } double mitk::VirtualTrackingDevice::GetMeanDistribution() { return m_MeanDistributionParam; } diff --git a/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.h b/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.h index 704f0ca810..253cbe3a12 100644 --- a/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.h +++ b/Modules/IGT/TrackingDevices/mitkVirtualTrackingDevice.h @@ -1,215 +1,214 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKVIRTUALTRACKINGDEVICE_H_HEADER_INCLUDED_ #define MITKVIRTUALTRACKINGDEVICE_H_HEADER_INCLUDED_ #include #include #include -#include -#include "itkFastMutexLock.h" +#include +#include #include namespace mitk { /** Documentation * \brief Class representing a tracking device which generates random positions / orientations. * No hardware is needed for tracking device. * * This TrackingDevice class does not interface with a physical tracking device. It simulates * a tracking device by moving the tools on a randomly generated spline path. * * \ingroup IGT */ class MITKIGT_EXPORT VirtualTrackingDevice : public TrackingDevice { public: mitkClassMacro(VirtualTrackingDevice, TrackingDevice); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * \brief Sets the refresh rate of the virtual tracking device in ms * \warning This refresh rate is not guaranteed. A thread is used to refresh the positions * of the virtual tools. However, this thread may not run at all during this refresh time period. * \return Sets the refresh rate of the virtual tracking device in ms */ itkSetMacro(RefreshRate, unsigned int); /** * \brief Returns the refresh rate in ms. * \return Returns the refresh rate in ms. */ itkGetConstMacro(RefreshRate, unsigned int); /** * \brief Starts the tracking. * * After StartTracking() is called, * the tools will move on their spline paths with a constant velocity that can be set with * SetToolSpeed(). The standard velocity is 10 seconds for one complete cycle along the spline path. * \warning tool speed is not yet used in the current version * \return Returns true if the tracking is started. Returns false if there was an error. */ bool StartTracking() override; /** * \brief Stops the tracking. * \return Returns true if the tracking is stopped. Returns false if there was an error. */ bool StopTracking() override; /** * \brief Opens the connection to the device. This have to be done before the tracking is started. * @throw mitk::IGTException Throws an exception if there are two less control points to start the the virtual device. */ bool OpenConnection() override; /** * \brief Closes the connection and clears all resources. */ bool CloseConnection() override; /** * \return Returns the number of tools which have been added to the device. */ unsigned int GetToolCount() const override; /** * \param toolNumber The number of the tool which should be given back. * \return Returns the tool which the number "toolNumber". Returns nullptr, if there is * no tool with this number. */ TrackingTool* GetTool(unsigned int toolNumber) const override; /** * \brief Adds a tool to the tracking device. * * The tool will have a random path on which it will move around. The path is created with a * spline function and random control points inside the tracking volume. * * \param toolName The tool which will be added. * \return Returns true if the tool has been added, false otherwise. */ TrackingTool* AddTool(const char* toolName); /** * \brief Set the tracking volume bounds * * This will set the tracking volume as an axis aligned bounding box * defined by the six bounds values xMin, xMax, yMin, yMax, zMin, zMax. * Note that the random path of existing tools will not be updated with the new * tracking volume. Tools that are created after calling SetBounds() will use the * new tracking volume */ itkSetVectorMacro(Bounds, mitk::ScalarType, 6); /** * \brief return the tracking volume bounds * * This will return the tracking volume as an axis aligned bounding box * defined by the six bounds values xMin, xMax, yMin, yMax, zMin, zMax */ const mitk::ScalarType* GetBounds() const { return m_Bounds; }; /** * \brief return the approximate length of the spline for tool with index idx in millimeter * * if the index idx is not a * valid tool index, a std::invalid_argument exception is thrown. * GetSplineChordLength() returns the distance between all control points of the * spline in millimeter. This can be used as an approximation for the length of the spline path. */ mitk::ScalarType GetSplineChordLength(unsigned int idx); /** * \brief sets the speed of the tool idx in rounds per second * * The virtual tools will travel along a closed spline path. * This method sets the speed of a tool as a factor of how many rounds per second * the tool should move. A setting of 1.0 will indicate one complete round per second. * Together with GetSplineChordLength(), the speed in millimeter per second can be estimated. * roundsPerSecond must be positive and larger than 0.0001. * \warning Tool speed is currently not used. * \todo use tool speed */ void SetToolSpeed(unsigned int idx, mitk::ScalarType roundsPerSecond); /** * \brief enable addition of Gaussian Noise to tracking coordinates */ void EnableGaussianNoise(); /** * \brief disable addition of Gaussian Noise to Trackin coordinates */ void DisableGaussianNoise(); /** * \brief sets the mean distribution and the standard deviation for the Gaussian Noise * */ void SetParamsForGaussianNoise(double meanDistribution, double deviationDistribution); /** * \brief returns the mean distribution for the Gaussian Noise */ double GetMeanDistribution(); /** * \brief returns the deviation distribution for the Gaussian Noise */ double GetDeviationDistribution(); protected: VirtualTrackingDevice(); ~VirtualTrackingDevice() override; /** * \brief This method tracks tools as long as the variable m_Mode is set to "Tracking". * Tracking tools means generating random numbers for the tool position and orientation. * @throw mitk::IGTException Throws an mitk::IGTException if there is an error during virtual tool tracking. */ void TrackTools(); void InitializeSpline(mitk::VirtualTrackingTool* t); ///< initializes the spline path of the tool t with random control points inside the current tracking volume - static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* data); ///< static start method for tracking thread + void ThreadStartTracking(); ///< static start method for tracking thread typedef mitk::VirtualTrackingTool::SplineType::ControlPointType ControlPointType; ControlPointType GetRandomPoint(); ///< returns a random position inside the tracking volume (defined by m_Bounds) mitk::VirtualTrackingTool* GetInternalTool(unsigned int idx); typedef std::vector ToolContainer; ///< container type for tracking tools ToolContainer m_AllTools; ///< container for all tracking tools - itk::FastMutexLock::Pointer m_ToolsMutex; ///< mutex for coordinated access of tool container + mutable std::mutex m_ToolsMutex; ///< mutex for coordinated access of tool container - itk::MultiThreader::Pointer m_MultiThreader; ///< MultiThreader that starts continuous tracking update - int m_ThreadID; + std::thread m_Thread; unsigned int m_RefreshRate; ///< refresh rate of the internal tracking thread in milliseconds (NOT refreshs per second!) unsigned int m_NumberOfControlPoints; ///< number of control points for the random path generation mitk::ScalarType m_Bounds[6]; ///< bounding box of the tracking volume stored as {xMin, xMax, yMin, yMax, zMin, zMax} bool m_GaussianNoiseEnabled; ///< adding Gaussian Noise to tracking coordinates or not, false by default double m_MeanDistributionParam; /// mean distribution for Gaussion Noise, 0.0 by default double m_DeviationDistributionParam; ///< deviation distribution for Gaussian Noise, 1.0 by default }; }//mitk #endif /* MITKVIRTUALTRACKINGDEVICE_H_HEADER_INCLUDED_ */ diff --git a/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.cpp b/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.cpp index 659d028447..9c36865587 100644 --- a/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.cpp +++ b/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.cpp @@ -1,25 +1,22 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkVirtualTrackingTool.h" -#include - -typedef itk::MutexLockHolder MutexLockHolder; mitk::VirtualTrackingTool::VirtualTrackingTool() : TrackingTool(), m_Spline(SplineType::New()), m_SplineLength(0.0), m_Velocity(0.1) { } mitk::VirtualTrackingTool::~VirtualTrackingTool() { } diff --git a/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h b/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h index a908baddba..89fe8fdf77 100644 --- a/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h +++ b/Modules/IGT/TrackingDevices/mitkVirtualTrackingTool.h @@ -1,63 +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 MITKVirtualTrackingTool_H_HEADER_INCLUDED_ #define MITKVirtualTrackingTool_H_HEADER_INCLUDED_ #include #include #include -#include #include namespace mitk { /**Documentation * \brief implements TrackingTool interface * * This class is a complete TrackingTool implementation. It can either be used directly by * TrackingDevices, or be subclassed for more specific implementations. * mitk::MicroBirdTrackingDevice uses this class to manage its tools. Other tracking devices * uses specialized versions of this class (e.g. mitk::NDITrackingTool) * * \ingroup IGT */ class MITKIGT_EXPORT VirtualTrackingTool : public TrackingTool { public: mitkClassMacro(VirtualTrackingTool, TrackingTool); friend class VirtualTrackingDevice; itkFactorylessNewMacro(Self) typedef itk::NonUniformBSpline<3> SplineType; ///< spline type used for tool path interpolation itkGetMacro(SplineLength, mitk::ScalarType); itkSetMacro(SplineLength, mitk::ScalarType); itkGetMacro(Velocity, mitk::ScalarType); itkSetMacro(Velocity, mitk::ScalarType); itkGetObjectMacro(Spline, SplineType); protected: itkCloneMacro(Self) VirtualTrackingTool(); ~VirtualTrackingTool() override; SplineType::Pointer m_Spline; mitk::ScalarType m_SplineLength; mitk::ScalarType m_Velocity; }; } // namespace mitk #endif /* MITKVirtualTrackingTool_H_HEADER_INCLUDED_ */ diff --git a/Modules/ImageDenoising/Testing/itkTotalVariationDenoisingImageFilterTest.cpp b/Modules/ImageDenoising/Testing/itkTotalVariationDenoisingImageFilterTest.cpp index ac6c9ca227..2b0622a437 100644 --- a/Modules/ImageDenoising/Testing/itkTotalVariationDenoisingImageFilterTest.cpp +++ b/Modules/ImageDenoising/Testing/itkTotalVariationDenoisingImageFilterTest.cpp @@ -1,271 +1,277 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "itkImageRegionIterator.h" #include "itkLocalVariationImageFilter.h" #include "itkTotalVariationDenoisingImageFilter.h" #include "itkTotalVariationSingleIterationImageFilter.h" // image typedefs typedef itk::Image ImageType; typedef itk::ImageRegionIterator IteratorType; // vector image typedefs typedef itk::Vector VectorPixelType; typedef itk::Image VectorImageType; typedef itk::ImageRegionIterator VectorIteratorType; /** * 3x3x3 test image */ ImageType::Pointer GenerateTestImage() { // init ImageType::Pointer image = ImageType::New(); ; // spacing ImageType::SpacingType spacing; spacing[0] = 1; spacing[1] = 1; spacing[2] = 1; image->SetSpacing(spacing); // extent ImageType::RegionType largestPossibleRegion; ImageType::SizeType size = {{3, 3, 1}}; largestPossibleRegion.SetSize(size); ImageType::IndexType index = {{0, 0, 0}}; largestPossibleRegion.SetIndex(index); image->SetLargestPossibleRegion(largestPossibleRegion); image->SetBufferedRegion(largestPossibleRegion); // allocate memory image->Allocate(); int i = 0; IteratorType it(image, largestPossibleRegion); it.GoToBegin(); while (!it.IsAtEnd()) { it.Set((float)i++); ++it; } return image; } VectorImageType::Pointer GenerateVectorTestImage() { // init VectorImageType::Pointer image = VectorImageType::New(); ; // spacing VectorImageType::SpacingType spacing; spacing[0] = 1; spacing[1] = 1; spacing[2] = 1; image->SetSpacing(spacing); // extent VectorImageType::RegionType largestPossibleRegion; VectorImageType::SizeType size = {{3, 3, 1}}; largestPossibleRegion.SetSize(size); VectorImageType::IndexType index = {{0, 0, 0}}; largestPossibleRegion.SetIndex(index); image->SetLargestPossibleRegion(largestPossibleRegion); image->SetBufferedRegion(largestPossibleRegion); // allocate memory image->Allocate(); int i = 0; VectorIteratorType it(image, largestPossibleRegion); it.GoToBegin(); while (!it.IsAtEnd()) { VectorPixelType vec; vec[0] = (float)i; vec[1] = (float)i++; it.Set(vec); ++it; } return image; } void PrintImage(ImageType::Pointer image) { IteratorType it(image, image->GetLargestPossibleRegion()); for (it.GoToBegin(); !it.IsAtEnd(); ++it) { std::cout << it.Get() << " "; } std::cout << std::endl; } void PrintVectorImage(VectorImageType::Pointer image) { VectorIteratorType it(image, image->GetLargestPossibleRegion()); for (it.GoToBegin(); !it.IsAtEnd(); ++it) { std::cout << it.Get() << " "; } std::cout << std::endl; } /** * todo */ int itkTotalVariationDenoisingImageFilterTest(int /*argc*/, char * /*argv*/ []) { ImageType::Pointer image = GenerateTestImage(); PrintImage(image); double precision = 0.01; ImageType::IndexType index = {{1, 1, 0}}; VectorImageType::IndexType vecIndex = {{1, 1, 0}}; try { typedef itk::LocalVariationImageFilter LocalFilterType; LocalFilterType::Pointer filter = LocalFilterType::New(); filter->SetInput(image); - filter->SetNumberOfThreads(1); + filter->SetNumberOfWorkUnits(1); filter->Update(); ImageType::Pointer outImage = filter->GetOutput(); PrintImage(outImage); if (fabs(outImage->GetPixel(index) - 4.472) > precision) { return EXIT_FAILURE; } } - catch (...) + catch (const itk::ExceptionObject& e) { + e.Print(std::cerr); return EXIT_FAILURE; } try { typedef itk::TotalVariationSingleIterationImageFilter SingleFilterType; SingleFilterType::Pointer sFilter = SingleFilterType::New(); sFilter->SetInput(image); sFilter->SetOriginalImage(GenerateTestImage()); sFilter->SetLambda(0.5); - sFilter->SetNumberOfThreads(1); + sFilter->SetNumberOfWorkUnits(1); sFilter->Update(); ImageType::Pointer outImageS = sFilter->GetOutput(); PrintImage(outImageS); if (fabs(outImageS->GetPixel(index) - 4.0) > precision) { return EXIT_FAILURE; } } - catch (...) + catch (const itk::ExceptionObject& e) { + e.Print(std::cerr); return EXIT_FAILURE; } try { typedef itk::TotalVariationDenoisingImageFilter TVFilterType; TVFilterType::Pointer tvFilter = TVFilterType::New(); tvFilter->SetInput(image); tvFilter->SetNumberIterations(30); - tvFilter->SetNumberOfThreads(1); + tvFilter->SetNumberOfWorkUnits(1); tvFilter->SetLambda(0.1); tvFilter->Update(); ImageType::Pointer outImageTV = tvFilter->GetOutput(); PrintImage(outImageTV); if (fabs(outImageTV->GetPixel(index) - 4.0) > precision) { return EXIT_FAILURE; } } - catch (...) + catch (const itk::ExceptionObject& e) { + e.Print(std::cerr); return EXIT_FAILURE; } VectorImageType::Pointer vecImage = GenerateVectorTestImage(); PrintVectorImage(vecImage); try { typedef itk::LocalVariationImageFilter LocalVecFilterType; LocalVecFilterType::Pointer vecFilter = LocalVecFilterType::New(); vecFilter->SetInput(vecImage); - vecFilter->SetNumberOfThreads(1); + vecFilter->SetNumberOfWorkUnits(1); vecFilter->Update(); ImageType::Pointer outVecImage = vecFilter->GetOutput(); PrintImage(outVecImage); if (fabs(outVecImage->GetPixel(index) - 6.324) > precision) { return EXIT_FAILURE; } } - catch (...) + catch (const itk::ExceptionObject& e) { + e.Print(std::cerr); return EXIT_FAILURE; } try { typedef itk::TotalVariationSingleIterationImageFilter SingleVecFilterType; SingleVecFilterType::Pointer sVecFilter = SingleVecFilterType::New(); sVecFilter->SetInput(vecImage); sVecFilter->SetOriginalImage(vecImage); sVecFilter->SetLambda(0.5); - sVecFilter->SetNumberOfThreads(1); + sVecFilter->SetNumberOfWorkUnits(1); sVecFilter->UpdateLargestPossibleRegion(); VectorImageType::Pointer outVecImageS = sVecFilter->GetOutput(); PrintVectorImage(outVecImageS); if (fabs(outVecImageS->GetPixel(vecIndex)[1] - 4.0) > precision) { return EXIT_FAILURE; } } - catch (...) + catch (const itk::ExceptionObject& e) { + e.Print(std::cerr); return EXIT_FAILURE; } try { typedef itk::TotalVariationDenoisingImageFilter TVVectorFilterType; TVVectorFilterType::Pointer tvVecFilter = TVVectorFilterType::New(); tvVecFilter->SetInput(vecImage); tvVecFilter->SetNumberIterations(30); - tvVecFilter->SetNumberOfThreads(1); + tvVecFilter->SetNumberOfWorkUnits(1); tvVecFilter->SetLambda(0.1); tvVecFilter->Update(); VectorImageType::Pointer outVecImageTV = tvVecFilter->GetOutput(); PrintVectorImage(outVecImageTV); if (fabs(outVecImageTV->GetPixel(vecIndex)[1] - 4.0) > precision) { return EXIT_FAILURE; } } - catch (...) + catch (const itk::ExceptionObject& e) { + e.Print(std::cerr); return EXIT_FAILURE; } return EXIT_SUCCESS; } diff --git a/Modules/ImageDenoising/itkLocalVariationImageFilter.txx b/Modules/ImageDenoising/itkLocalVariationImageFilter.txx index 679247d56b..be84c917bb 100644 --- a/Modules/ImageDenoising/itkLocalVariationImageFilter.txx +++ b/Modules/ImageDenoising/itkLocalVariationImageFilter.txx @@ -1,188 +1,189 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef _itkLocalVariationImageFilter_txx #define _itkLocalVariationImageFilter_txx #include "itkLocalVariationImageFilter.h" #include "itkConstShapedNeighborhoodIterator.h" #include "itkImageRegionIterator.h" #include "itkNeighborhoodAlgorithm.h" #include "itkNeighborhoodInnerProduct.h" #include "itkOffset.h" #include "itkProgressReporter.h" #include "itkVectorImage.h" #include "itkZeroFluxNeumannBoundaryCondition.h" #include #include namespace itk { template LocalVariationImageFilter::LocalVariationImageFilter() { + this->DynamicMultiThreadingOff(); } template void LocalVariationImageFilter::GenerateInputRequestedRegion() { // call the superclass' implementation of this method Superclass::GenerateInputRequestedRegion(); // get pointers to the input and output typename Superclass::InputImagePointer inputPtr = const_cast(this->GetInput()); typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); if (!inputPtr || !outputPtr) { return; } // get a copy of the input requested region (should equal the output // requested region) typename TInputImage::RegionType inputRequestedRegion; inputRequestedRegion = inputPtr->GetRequestedRegion(); // pad the input requested region by 1 inputRequestedRegion.PadByRadius(1); // crop the input requested region at the input's largest possible region if (inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion())) { inputPtr->SetRequestedRegion(inputRequestedRegion); return; } else { // Couldn't crop the region (requested region is outside the largest // possible region). Throw an exception. // store what we tried to request (prior to trying to crop) inputPtr->SetRequestedRegion(inputRequestedRegion); // build an exception InvalidRequestedRegionError e(__FILE__, __LINE__); e.SetLocation(ITK_LOCATION); e.SetDescription("Requested region outside possible region."); e.SetDataObject(inputPtr); throw e; } } template <> double SquaredEuclideanMetric>::Calc(itk::VariableLengthVector p) { return p.GetSquaredNorm(); } template <> double SquaredEuclideanMetric>::Calc(itk::VariableLengthVector p) { return p.GetSquaredNorm(); } template double SquaredEuclideanMetric::Calc(TPixelType p) { return p * p; } template void LocalVariationImageFilter::ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, ThreadIdType threadId) { // Allocate output typename OutputImageType::Pointer output = this->GetOutput(); typename InputImageType::ConstPointer input = this->GetInput(); itk::Size size; for (unsigned int i = 0; i < InputImageDimension; i++) size[i] = 1; // Find the data-set boundary "faces" NeighborhoodAlgorithm::ImageBoundaryFacesCalculator bC; typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator::FaceListType faceList = bC(input, outputRegionForThread, size); // support progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels()); ZeroFluxNeumannBoundaryCondition nbc; std::vector pixels; // Process each of the boundary faces. These are N-d regions which border // the edge of the buffer. for (auto fit = faceList.begin(); fit != faceList.end(); ++fit) { // iterators over output and input ImageRegionIterator output_image_it(output, *fit); ImageRegionConstIterator input_image_it(input.GetPointer(), *fit); // neighborhood iterator for input image ConstShapedNeighborhoodIterator input_image_neighbors_it(size, input, *fit); typename ConstShapedNeighborhoodIterator::OffsetType offset; input_image_neighbors_it.OverrideBoundaryCondition(&nbc); input_image_neighbors_it.ClearActiveList(); for (unsigned int i = 0; i < InputImageDimension; i++) { offset.Fill(0); offset[i] = -1; input_image_neighbors_it.ActivateOffset(offset); offset[i] = 1; input_image_neighbors_it.ActivateOffset(offset); } input_image_neighbors_it.GoToBegin(); // const unsigned int neighborhoodSize = InputImageDimension*2; while (!input_image_neighbors_it.IsAtEnd()) { // collect all the pixels in the neighborhood, note that we use // GetPixel on the NeighborhoodIterator to honor the boundary conditions typename OutputImageType::PixelType locVariation = 0; typename ConstShapedNeighborhoodIterator::ConstIterator input_neighbors_it; for (input_neighbors_it = input_image_neighbors_it.Begin(); !input_neighbors_it.IsAtEnd(); input_neighbors_it++) { typename TInputImage::PixelType diffVec = input_neighbors_it.Get() - input_image_it.Get(); locVariation += SquaredEuclideanMetric::Calc(diffVec); } locVariation = sqrt(locVariation + 0.0001); output_image_it.Set(locVariation); // update iterators ++input_image_neighbors_it; ++output_image_it; ++input_image_it; // report progress progress.CompletedPixel(); } } } /** * Standard "PrintSelf" method */ template void LocalVariationImageFilter::PrintSelf(std::ostream &os, Indent indent) const { Superclass::PrintSelf(os, indent); } } // end namespace itk #endif //_itkLocalVariationImageFilter_txx diff --git a/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx b/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx index d5b776752b..25d077d79e 100644 --- a/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx +++ b/Modules/ImageDenoising/itkTotalVariationDenoisingImageFilter.txx @@ -1,105 +1,105 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef _itkTotalVariationDenoisingImageFilter_txx #define _itkTotalVariationDenoisingImageFilter_txx #include "itkTotalVariationDenoisingImageFilter.h" #include "itkConstShapedNeighborhoodIterator.h" #include "itkImageRegionConstIterator.h" #include "itkImageRegionIterator.h" #include "itkLocalVariationImageFilter.h" #include "itkNeighborhoodAlgorithm.h" #include "itkNeighborhoodInnerProduct.h" #include "itkOffset.h" #include "itkProgressReporter.h" #include "itkZeroFluxNeumannBoundaryCondition.h" #include #include namespace itk { template TotalVariationDenoisingImageFilter::TotalVariationDenoisingImageFilter() : m_Lambda(1.0), m_NumberIterations(0) { } template void TotalVariationDenoisingImageFilter::GenerateData() { // first we cast the input image to match output type typename CastType::Pointer infilter = CastType::New(); infilter->SetInput(this->GetInput()); infilter->Update(); typename TOutputImage::Pointer image = infilter->GetOutput(); // a second copy of the input image is saved as reference infilter = CastType::New(); infilter->SetInput(this->GetInput()); infilter->Update(); typename TOutputImage::Pointer origImage = infilter->GetOutput(); typename SingleIterationFilterType::Pointer filter; for (int i = 0; i < m_NumberIterations; i++) { filter = SingleIterationFilterType::New(); filter->SetInput(image); filter->SetOriginalImage(origImage); filter->SetLambda(m_Lambda); - filter->SetNumberOfThreads(this->GetNumberOfThreads()); + filter->SetNumberOfWorkUnits(this->GetNumberOfWorkUnits()); filter->UpdateLargestPossibleRegion(); image = filter->GetOutput(); std::cout << "Iteration " << i + 1 << "/" << m_NumberIterations << std::endl; } typename OutputImageType::Pointer output = this->GetOutput(); output->SetSpacing(image->GetSpacing()); typename OutputImageType::RegionType largestPossibleRegion; largestPossibleRegion.SetSize(image->GetLargestPossibleRegion().GetSize()); largestPossibleRegion.SetIndex(image->GetLargestPossibleRegion().GetIndex()); output->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); output->SetBufferedRegion(image->GetLargestPossibleRegion()); output->Allocate(); itk::ImageRegionIterator oit(output, output->GetLargestPossibleRegion()); oit.GoToBegin(); itk::ImageRegionConstIterator iit(image, image->GetLargestPossibleRegion()); iit.GoToBegin(); while (!oit.IsAtEnd()) { oit.Set(iit.Get()); ++iit; ++oit; } } /** * Standard "PrintSelf" method */ template void TotalVariationDenoisingImageFilter::PrintSelf(std::ostream &os, Indent indent) const { Superclass::PrintSelf(os, indent); } } // end namespace itk #endif diff --git a/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx b/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx index a8fa6f6063..a069c1d957 100644 --- a/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx +++ b/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx @@ -1,251 +1,252 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef _itkTotalVariationSingleIterationImageFilter_txx #define _itkTotalVariationSingleIterationImageFilter_txx #include "itkTotalVariationSingleIterationImageFilter.h" // itk includes #include "itkConstShapedNeighborhoodIterator.h" #include "itkImageRegionIterator.h" #include "itkLocalVariationImageFilter.h" #include "itkNeighborhoodAlgorithm.h" #include "itkNeighborhoodInnerProduct.h" #include "itkOffset.h" #include "itkProgressReporter.h" #include "itkZeroFluxNeumannBoundaryCondition.h" // other includes #include #include namespace itk { /** * constructor */ template TotalVariationSingleIterationImageFilter::TotalVariationSingleIterationImageFilter() { + this->DynamicMultiThreadingOff(); m_Lambda = 1.0; m_LocalVariation = LocalVariationImageType::New(); } /** * generate requested region */ template void TotalVariationSingleIterationImageFilter::GenerateInputRequestedRegion() { // call the superclass' implementation of this method Superclass::GenerateInputRequestedRegion(); // get pointers to the input and output typename Superclass::InputImagePointer inputPtr = const_cast(this->GetInput()); typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); if (!inputPtr || !outputPtr) { return; } // get a copy of the input requested region (should equal the output // requested region) typename TInputImage::RegionType inputRequestedRegion; inputRequestedRegion = inputPtr->GetRequestedRegion(); // pad the input requested region by 1 inputRequestedRegion.PadByRadius(1); // crop the input requested region at the input's largest possible region if (inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion())) { inputPtr->SetRequestedRegion(inputRequestedRegion); return; } else { // Couldn't crop the region (requested region is outside the largest // possible region). Throw an exception. // store what we tried to request (prior to trying to crop) inputPtr->SetRequestedRegion(inputRequestedRegion); // build an exception InvalidRequestedRegionError e(__FILE__, __LINE__); e.SetLocation(ITK_LOCATION); e.SetDescription("Requested region outside possible region."); e.SetDataObject(inputPtr); throw e; } } /** * generate output */ template void TotalVariationSingleIterationImageFilter::ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, ThreadIdType threadId) { typename OutputImageType::Pointer output = this->GetOutput(); typename InputImageType::ConstPointer input = this->GetInput(); // Find the data-set boundary "faces" itk::Size size; for (unsigned int i = 0; i < InputImageDimension; i++) size[i] = 1; NeighborhoodAlgorithm::ImageBoundaryFacesCalculator bC; typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator::FaceListType faceList = bC(input, outputRegionForThread, size); NeighborhoodAlgorithm::ImageBoundaryFacesCalculator lv_bC; typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator::FaceListType lv_faceList = lv_bC(m_LocalVariation, outputRegionForThread, size); // support progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels()); ZeroFluxNeumannBoundaryCondition nbc; ZeroFluxNeumannBoundaryCondition lv_nbc; std::vector ws; std::vector hs; auto lv_fit = lv_faceList.begin(); // Process each of the boundary faces. These are N-d regions which border // the edge of the buffer. for (auto fit = faceList.begin(); fit != faceList.end(); ++fit) { // iterators over output, input, original and local variation image ImageRegionIterator output_image_it = ImageRegionIterator(output, *fit); ImageRegionConstIterator input_image_it = ImageRegionConstIterator(input, *fit); ImageRegionConstIterator orig_image_it = ImageRegionConstIterator(m_OriginalImage, *fit); ImageRegionConstIterator loc_var_image_it = ImageRegionConstIterator(m_LocalVariation, *fit); // neighborhood in input image ConstShapedNeighborhoodIterator input_image_neighbors_it(size, input, *fit); typename ConstShapedNeighborhoodIterator::OffsetType offset; input_image_neighbors_it.OverrideBoundaryCondition(&nbc); input_image_neighbors_it.ClearActiveList(); for (unsigned int i = 0; i < InputImageDimension; i++) { offset.Fill(0); offset[i] = -1; input_image_neighbors_it.ActivateOffset(offset); offset[i] = 1; input_image_neighbors_it.ActivateOffset(offset); } input_image_neighbors_it.GoToBegin(); // neighborhood in local variation image ConstShapedNeighborhoodIterator loc_var_image_neighbors_it( size, m_LocalVariation, *lv_fit); loc_var_image_neighbors_it.OverrideBoundaryCondition(&lv_nbc); loc_var_image_neighbors_it.ClearActiveList(); for (unsigned int i = 0; i < InputImageDimension; i++) { offset.Fill(0); offset[i] = -1; loc_var_image_neighbors_it.ActivateOffset(offset); offset[i] = 1; loc_var_image_neighbors_it.ActivateOffset(offset); } loc_var_image_neighbors_it.GoToBegin(); const unsigned int neighborhoodSize = InputImageDimension * 2; ws.resize(neighborhoodSize); while (!output_image_it.IsAtEnd()) { // 1 / ||nabla_alpha(u)||_a double locvar_alpha_inv = 1.0 / loc_var_image_it.Get(); // compute w_alphabetas int count = 0; double wsum = 0; typename ConstShapedNeighborhoodIterator::ConstIterator loc_var_neighbors_it; for (loc_var_neighbors_it = loc_var_image_neighbors_it.Begin(); !loc_var_neighbors_it.IsAtEnd(); loc_var_neighbors_it++) { // w_alphabeta(u) = // 1 / ||nabla_alpha(u)||_a + 1 / ||nabla_beta(u)||_a ws[count] = locvar_alpha_inv + (1.0 / (double)loc_var_neighbors_it.Get()); wsum += ws[count++]; } // h_alphaalpha * u_alpha^zero typename OutputImageType::PixelType res = static_cast( ((typename OutputImageType::PixelType)orig_image_it.Get()) * (m_Lambda / (m_Lambda + wsum))); // add the different h_alphabeta * u_beta count = 0; typename ConstShapedNeighborhoodIterator::ConstIterator input_neighbors_it; for (input_neighbors_it = input_image_neighbors_it.Begin(); !input_neighbors_it.IsAtEnd(); input_neighbors_it++) { res += input_neighbors_it.Get() * (ws[count++] / (m_Lambda + wsum)); } // set output result output_image_it.Set(res); // increment iterators ++output_image_it; ++input_image_it; ++orig_image_it; ++loc_var_image_it; ++input_image_neighbors_it; ++loc_var_image_neighbors_it; // report progress progress.CompletedPixel(); } ++lv_fit; } } /** * first calculate local variation in the image */ template void TotalVariationSingleIterationImageFilter::BeforeThreadedGenerateData() { typedef typename itk::LocalVariationImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput(this->GetInput(0)); - filter->SetNumberOfThreads(this->GetNumberOfThreads()); + filter->SetNumberOfWorkUnits(this->GetNumberOfWorkUnits()); filter->Update(); this->m_LocalVariation = filter->GetOutput(); } /** * Standard "PrintSelf" method */ template void TotalVariationSingleIterationImageFilter::PrintSelf(std::ostream &os, Indent indent) const { Superclass::PrintSelf(os, indent); } } // end namespace itk #endif diff --git a/Modules/ImageExtraction/mitkExtractImageFilter.cpp b/Modules/ImageExtraction/mitkExtractImageFilter.cpp index e23f3f9537..1115c6ebd2 100644 --- a/Modules/ImageExtraction/mitkExtractImageFilter.cpp +++ b/Modules/ImageExtraction/mitkExtractImageFilter.cpp @@ -1,259 +1,259 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkExtractImageFilter.h" #include "mitkITKImageImport.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "mitkPlaneGeometry.h" #include #include mitk::ExtractImageFilter::ExtractImageFilter() : m_SliceIndex(0), m_SliceDimension(0), m_TimeStep(0), m_DirectionCollapseToStrategy(DIRECTIONCOLLAPSETOGUESS) { MITK_WARN << "Class ExtractImageFilter is deprecated! Use ExtractSliceFilter instead."; } mitk::ExtractImageFilter::~ExtractImageFilter() { } void mitk::ExtractImageFilter::GenerateData() { Image::ConstPointer input = ImageToImageFilter::GetInput(0); if ((input->GetDimension() > 4) || (input->GetDimension() < 2)) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateData works only with 3D and 3D+t images, sorry." << std::endl; itkExceptionMacro("mitk::ExtractImageFilter works only with 3D and 3D+t images, sorry."); return; } else if (input->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(input); timeSelector->SetTimeNr(m_TimeStep); timeSelector->UpdateLargestPossibleRegion(); input = timeSelector->GetOutput(); } else if (input->GetDimension() == 2) { Image::Pointer resultImage = ImageToImageFilter::GetOutput(); resultImage = const_cast(input.GetPointer()); ImageToImageFilter::SetNthOutput(0, resultImage); return; } if (m_SliceDimension >= input->GetDimension()) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateData m_SliceDimension == " << m_SliceDimension << " makes no sense with an " << input->GetDimension() << "D image." << std::endl; itkExceptionMacro("This is not a sensible value for m_SliceDimension."); return; } AccessFixedDimensionByItk(input, ItkImageProcessing, 3); // set a nice geometry for display and point transformations BaseGeometry *inputImageGeometry = ImageToImageFilter::GetInput(0)->GetGeometry(); if (!inputImageGeometry) { MITK_ERROR << "In ExtractImageFilter::ItkImageProcessing: Input image has no geometry!" << std::endl; return; } PlaneGeometry::PlaneOrientation orientation = PlaneGeometry::Axial; switch (m_SliceDimension) { default: case 2: orientation = PlaneGeometry::Axial; break; case 1: orientation = PlaneGeometry::Frontal; break; case 0: orientation = PlaneGeometry::Sagittal; break; } PlaneGeometry::Pointer planeGeometry = PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(inputImageGeometry, orientation, (ScalarType)m_SliceIndex, true, false); Image::Pointer resultImage = ImageToImageFilter::GetOutput(); planeGeometry->ChangeImageGeometryConsideringOriginOffset(true); resultImage->SetGeometry(planeGeometry); } template void mitk::ExtractImageFilter::ItkImageProcessing(const itk::Image *itkImage) { // use the itk::ExtractImageFilter to get a 2D image typedef itk::Image ImageType3D; typedef itk::Image ImageType2D; typedef itk::ExtractImageFilter ExtractImageFilterType; typename ImageType3D::RegionType inSliceRegion = itkImage->GetLargestPossibleRegion(); inSliceRegion.SetSize(m_SliceDimension, 0); typename ExtractImageFilterType::Pointer sliceExtractor = ExtractImageFilterType::New(); - typename ExtractImageFilterType::DIRECTIONCOLLAPSESTRATEGY collapseStrategy; + typename ExtractImageFilterType::DirectionCollapseStrategyEnum collapseStrategy; switch (m_DirectionCollapseToStrategy) { case DIRECTIONCOLLAPSETOUNKOWN: - collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOUNKOWN; + collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOUNKOWN; break; case DIRECTIONCOLLAPSETOIDENTITY: - collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOIDENTITY; + collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOIDENTITY; break; case DIRECTIONCOLLAPSETOSUBMATRIX: - collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOSUBMATRIX; + collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOSUBMATRIX; break; case DIRECTIONCOLLAPSETOGUESS: default: - collapseStrategy = ExtractImageFilterType::DIRECTIONCOLLAPSETOGUESS; + collapseStrategy = ExtractImageFilterType::DirectionCollapseStrategyEnum::DIRECTIONCOLLAPSETOGUESS; break; } sliceExtractor->SetDirectionCollapseToStrategy(collapseStrategy); sliceExtractor->SetInput(itkImage); inSliceRegion.SetIndex(m_SliceDimension, m_SliceIndex); sliceExtractor->SetExtractionRegion(inSliceRegion); // calculate the output sliceExtractor->UpdateLargestPossibleRegion(); typename ImageType2D::Pointer slice = sliceExtractor->GetOutput(); // re-import to MITK Image::Pointer resultImage = ImageToImageFilter::GetOutput(); GrabItkImageMemory(slice, resultImage, nullptr, false); } /* * What is the input requested region that is required to produce the output * requested region? By default, the largest possible region is always * required but this is overridden in many subclasses. For instance, for an * image processing filter where an output pixel is a simple function of an * input pixel, the input requested region will be set to the output * requested region. For an image processing filter where an output pixel is * a function of the pixels in a neighborhood of an input pixel, then the * input requested region will need to be larger than the output requested * region (to avoid introducing artificial boundary conditions). This * function should never request an input region that is outside the the * input largest possible region (i.e. implementations of this method should * crop the input requested region at the boundaries of the input largest * possible region). */ void mitk::ExtractImageFilter::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); ImageToImageFilter::InputImagePointer input = dynamic_cast(this->GetInput()); Image::Pointer output = this->GetOutput(); if (input->GetDimension() == 2) { input->SetRequestedRegionToLargestPossibleRegion(); return; } Image::RegionType requestedRegion; requestedRegion = output->GetRequestedRegion(); requestedRegion.SetIndex(0, 0); requestedRegion.SetIndex(1, 0); requestedRegion.SetIndex(2, 0); requestedRegion.SetSize(0, input->GetDimension(0)); requestedRegion.SetSize(1, input->GetDimension(1)); requestedRegion.SetSize(2, input->GetDimension(2)); requestedRegion.SetIndex(m_SliceDimension, m_SliceIndex); // only one slice needed requestedRegion.SetSize(m_SliceDimension, 1); input->SetRequestedRegion(&requestedRegion); } /* * Generate the information decribing the output data. The default * implementation of this method will copy information from the input to the * output. A filter may override this method if its output will have different * information than its input. For instance, a filter that shrinks an image will * need to provide an implementation for this method that changes the spacing of * the pixels. Such filters should call their superclass' implementation of this * method prior to changing the information values they need (i.e. * GenerateOutputInformation() should call * Superclass::GenerateOutputInformation() prior to changing the information. */ void mitk::ExtractImageFilter::GenerateOutputInformation() { Image::Pointer output = this->GetOutput(); Image::ConstPointer input = this->GetInput(); if (input.IsNull()) return; if (m_SliceDimension >= input->GetDimension() && input->GetDimension() != 2) { MITK_ERROR << "mitk::ExtractImageFilter:GenerateOutputInformation m_SliceDimension == " << m_SliceDimension << " makes no sense with an " << input->GetDimension() << "D image." << std::endl; itkExceptionMacro("This is not a sensible value for m_SliceDimension."); return; } unsigned int sliceDimension(m_SliceDimension); if (input->GetDimension() == 2) { sliceDimension = 2; } unsigned int tmpDimensions[2]; switch (sliceDimension) { default: case 2: // orientation = PlaneGeometry::Axial; tmpDimensions[0] = input->GetDimension(0); tmpDimensions[1] = input->GetDimension(1); break; case 1: // orientation = PlaneGeometry::Frontal; tmpDimensions[0] = input->GetDimension(0); tmpDimensions[1] = input->GetDimension(2); break; case 0: // orientation = PlaneGeometry::Sagittal; tmpDimensions[0] = input->GetDimension(1); tmpDimensions[1] = input->GetDimension(2); break; } output->Initialize(input->GetPixelType(), 2, tmpDimensions, 1 /*input->GetNumberOfChannels()*/); // initialize the spacing of the output /* Vector3D spacing = input->GetSlicedGeometry()->GetSpacing(); if(input->GetDimension()>=2) spacing[2]=spacing[1]; else spacing[2] = 1.0; output->GetSlicedGeometry()->SetSpacing(spacing); */ output->SetPropertyList(input->GetPropertyList()->Clone()); } diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp index 164906747a..cfbb184611 100644 --- a/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp +++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp @@ -1,1139 +1,1141 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkImageStatisticsCalculator.h" #include #include #include #include #include #include #include /** * \brief Test class for mitkImageStatisticsCalculator * * This test covers: * - instantiation of an ImageStatisticsCalculator class * - correctness of statistics when using PlanarFigures for masking */ class mitkImageStatisticsCalculatorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkImageStatisticsCalculatorTestSuite); MITK_TEST(TestUninitializedImage); MITK_TEST(TestCase1); MITK_TEST(TestCase2); MITK_TEST(TestCase3); MITK_TEST(TestCase4); MITK_TEST(TestCase5); MITK_TEST(TestCase6); MITK_TEST(TestCase7); MITK_TEST(TestCase8); MITK_TEST(TestCase9); MITK_TEST(TestCase10); MITK_TEST(TestCase11); MITK_TEST(TestCase12); MITK_TEST(TestPic3DCroppedNoMask); MITK_TEST(TestPic3DCroppedBinMask); MITK_TEST(TestPic3DCroppedMultilabelMask); MITK_TEST(TestPic3DCroppedPlanarFigure); MITK_TEST(TestUS4DCroppedNoMaskTimeStep1); MITK_TEST(TestUS4DCroppedBinMaskTimeStep1); MITK_TEST(TestUS4DCroppedMultilabelMaskTimeStep1); MITK_TEST(TestUS4DCroppedPlanarFigureTimeStep1); MITK_TEST(TestUS4DCroppedAllTimesteps); MITK_TEST(TestUS4DCropped3DMask); CPPUNIT_TEST_SUITE_END(); public: void TestUninitializedImage(); void TestCase1(); void TestCase2(); void TestCase3(); void TestCase4(); void TestCase5(); void TestCase6(); void TestCase7(); void TestCase8(); void TestCase9(); void TestCase10(); void TestCase11(); void TestCase12(); void TestPic3DCroppedNoMask(); void TestPic3DCroppedBinMask(); void TestPic3DCroppedMultilabelMask(); void TestPic3DCroppedPlanarFigure(); void TestUS4DCroppedNoMaskTimeStep1(); void TestUS4DCroppedBinMaskTimeStep1(); void TestUS4DCroppedMultilabelMaskTimeStep1(); void TestUS4DCroppedPlanarFigureTimeStep1(); void TestUS4DCroppedAllTimesteps(); void TestUS4DCropped3DMask(); private: - mitk::Image::ConstPointer m_TestImage; - - mitk::Image::ConstPointer m_Pic3DCroppedImage; - mitk::Image::Pointer m_Pic3DCroppedBinMask; - mitk::Image::Pointer m_Pic3DCroppedMultilabelMask; - mitk::PlanarFigure::Pointer m_Pic3DCroppedPlanarFigure; - - mitk::Image::ConstPointer m_US4DCroppedImage; - mitk::Image::Pointer m_US4DCroppedBinMask; - mitk::Image::Pointer m_US4DCroppedMultilabelMask; - mitk::Image::Pointer m_US4DCropped3DBinMask; - mitk::PlanarFigure::Pointer m_US4DCroppedPlanarFigure; - - mitk::PlaneGeometry::Pointer m_Geometry; - - // creates a polygon given a geometry and a vector of 2d points - mitk::PlanarPolygon::Pointer GeneratePlanarPolygon(mitk::PlaneGeometry::Pointer geometry, std::vector points); - - // universal function to calculate statistics - const mitk::ImageStatisticsContainer::Pointer ComputeStatistics(mitk::Image::ConstPointer image, - mitk::MaskGenerator::Pointer maskGen = nullptr, - mitk::MaskGenerator::Pointer secondardMaskGen = nullptr, - unsigned short label = 1); - - void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats, - mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian = 0); - - // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) - void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats, - mitk::ImageStatisticsContainer::VoxelCountType N, - mitk::ImageStatisticsContainer::RealType mean, - mitk::ImageStatisticsContainer::RealType MPP, - mitk::ImageStatisticsContainer::RealType skewness, - mitk::ImageStatisticsContainer::RealType kurtosis, - mitk::ImageStatisticsContainer::RealType variance, - mitk::ImageStatisticsContainer::RealType stdev, - mitk::ImageStatisticsContainer::RealType min, - mitk::ImageStatisticsContainer::RealType max, - mitk::ImageStatisticsContainer::RealType RMS, - mitk::ImageStatisticsContainer::IndexType minIndex, - mitk::ImageStatisticsContainer::IndexType maxIndex); + mitk::Image::ConstPointer m_TestImage; + + mitk::Image::ConstPointer m_Pic3DCroppedImage; + mitk::Image::Pointer m_Pic3DCroppedBinMask; + mitk::Image::Pointer m_Pic3DCroppedMultilabelMask; + mitk::PlanarFigure::Pointer m_Pic3DCroppedPlanarFigure; + + mitk::Image::ConstPointer m_US4DCroppedImage; + mitk::Image::Pointer m_US4DCroppedBinMask; + mitk::Image::Pointer m_US4DCroppedMultilabelMask; + mitk::Image::Pointer m_US4DCropped3DBinMask; + mitk::PlanarFigure::Pointer m_US4DCroppedPlanarFigure; + + mitk::PlaneGeometry::Pointer m_Geometry; + + // creates a polygon given a geometry and a vector of 2d points + mitk::PlanarPolygon::Pointer GeneratePlanarPolygon(mitk::PlaneGeometry::Pointer geometry, std::vector points); + + // universal function to calculate statistics + const mitk::ImageStatisticsContainer::Pointer ComputeStatistics(mitk::Image::ConstPointer image, + mitk::MaskGenerator::Pointer maskGen = nullptr, + mitk::MaskGenerator::Pointer secondardMaskGen = nullptr, + unsigned short label = 1); + + void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats, + mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian = 0); + + // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) + void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats, + mitk::ImageStatisticsContainer::VoxelCountType N, + mitk::ImageStatisticsContainer::RealType mean, + mitk::ImageStatisticsContainer::RealType MPP, + mitk::ImageStatisticsContainer::RealType skewness, + mitk::ImageStatisticsContainer::RealType kurtosis, + mitk::ImageStatisticsContainer::RealType variance, + mitk::ImageStatisticsContainer::RealType stdev, + mitk::ImageStatisticsContainer::RealType min, + mitk::ImageStatisticsContainer::RealType max, + mitk::ImageStatisticsContainer::RealType RMS, + mitk::ImageStatisticsContainer::IndexType minIndex, + mitk::ImageStatisticsContainer::IndexType maxIndex); }; void mitkImageStatisticsCalculatorTestSuite::TestUninitializedImage() { - /***************************** - * loading uninitialized image to datastorage - ******************************/ - MITK_INFO << std::endl << "Test uninitialized image: -----------------------------------------------------------------------------------"; - mitk::Image::Pointer image = mitk::Image::New(); - mitk::DataNode::Pointer node = mitk::DataNode::New(); - node->SetData(image); - - mitk::ImageStatisticsCalculator::Pointer is = mitk::ImageStatisticsCalculator::New(); - CPPUNIT_ASSERT_THROW(is->GetStatistics(), mitk::Exception); + /***************************** + * loading uninitialized image to datastorage + ******************************/ + MITK_INFO << std::endl << "Test uninitialized image: -----------------------------------------------------------------------------------"; + mitk::Image::Pointer image = mitk::Image::New(); + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData(image); + + mitk::ImageStatisticsCalculator::Pointer is = mitk::ImageStatisticsCalculator::New(); + CPPUNIT_ASSERT_THROW(is->GetStatistics(), mitk::Exception); } void mitkImageStatisticsCalculatorTestSuite::TestCase1() { - /***************************** - * one whole white pixel - * -> mean of 255 expected - ******************************/ - MITK_INFO << std::endl << "Test case 1:-----------------------------------------------------------------------------------"; - - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - CPPUNIT_ASSERT_MESSAGE("Failed loading an mitk::Image", m_TestImage.IsNotNull()); - - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - CPPUNIT_ASSERT_MESSAGE("Failed getting image geometry", m_Geometry.IsNotNull()); - - mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5; - mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; - mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; - mitk::Point2D pnt4; pnt4[0] = 10.5; pnt4[1] = 4.5; - std::vector points{ pnt1,pnt2,pnt3,pnt4 }; + /***************************** + * one whole white pixel + * -> mean of 255 expected + ******************************/ + MITK_INFO << std::endl << "Test case 1:-----------------------------------------------------------------------------------"; + + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + CPPUNIT_ASSERT_MESSAGE("Failed loading an mitk::Image", m_TestImage.IsNotNull()); + + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + CPPUNIT_ASSERT_MESSAGE("Failed getting image geometry", m_Geometry.IsNotNull()); + + mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5; + mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; + mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; + mitk::Point2D pnt4; pnt4[0] = 10.5; pnt4[1] = 4.5; + std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; + mitk::ImageStatisticsContainer::Pointer statisticsContainer; - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0); + this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase2() { - /***************************** - * half pixel in x-direction (white) - * -> mean of 255 expected - ******************************/ - MITK_INFO << std::endl << "Test case 2:-----------------------------------------------------------------------------------"; - - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - - mitk::Point2D pnt1; pnt1[0] = 10.0; pnt1[1] = 3.5; - mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; - mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; - mitk::Point2D pnt4; pnt4[0] = 10.0; pnt4[1] = 4.5; - std::vector points{ pnt1,pnt2,pnt3,pnt4 }; + /***************************** + * half pixel in x-direction (white) + * -> mean of 255 expected + ******************************/ + MITK_INFO << std::endl << "Test case 2:-----------------------------------------------------------------------------------"; + + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + + mitk::Point2D pnt1; pnt1[0] = 10.0; pnt1[1] = 3.5; + mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; + mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; + mitk::Point2D pnt4; pnt4[0] = 10.0; pnt4[1] = 4.5; + std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0); + this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase3() { - /***************************** - * half pixel in diagonal-direction (white) - * -> mean of 255 expected - ******************************/ - MITK_INFO << std::endl << "Test case 3:-----------------------------------------------------------------------------------"; - - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - - mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5; - mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; - mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; - std::vector points{ pnt1,pnt2,pnt3 }; + /***************************** + * half pixel in diagonal-direction (white) + * -> mean of 255 expected + ******************************/ + MITK_INFO << std::endl << "Test case 3:-----------------------------------------------------------------------------------"; + + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + + mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5; + mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; + mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; + std::vector points{ pnt1,pnt2,pnt3 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0); + this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase4() { - /***************************** - * one pixel (white) + 2 half pixels (white) + 1 half pixel (black) - * -> mean of 191.25 expected - ******************************/ - MITK_INFO << std::endl << "Test case 4:-----------------------------------------------------------------------------------"; - - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - - mitk::Point2D pnt1; pnt1[0] = 1.1; pnt1[1] = 1.1; - mitk::Point2D pnt2; pnt2[0] = 2.0; pnt2[1] = 2.0; - mitk::Point2D pnt3; pnt3[0] = 3.0; pnt3[1] = 1.0; - mitk::Point2D pnt4; pnt4[0] = 2.0; pnt4[1] = 0.0; - std::vector points{ pnt1,pnt2,pnt3,pnt4 }; + /***************************** + * one pixel (white) + 2 half pixels (white) + 1 half pixel (black) + * -> mean of 191.25 expected + ******************************/ + MITK_INFO << std::endl << "Test case 4:-----------------------------------------------------------------------------------"; + + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + + mitk::Point2D pnt1; pnt1[0] = 1.1; pnt1[1] = 1.1; + mitk::Point2D pnt2; pnt2[0] = 2.0; pnt2[1] = 2.0; + mitk::Point2D pnt3; pnt3[0] = 3.0; pnt3[1] = 1.0; + mitk::Point2D pnt4; pnt4[0] = 2.0; pnt4[1] = 0.0; + std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 191.25, 110.41823898251593, 253.72499847412109); + this->VerifyStatistics(statisticsObjectTimestep0, 191.25, 127.5, 253.72499847412109); } void mitkImageStatisticsCalculatorTestSuite::TestCase5() { - /***************************** - * whole pixel (white) + half pixel (gray) in x-direction - * -> mean of 191.5 expected - ******************************/ - MITK_INFO << std::endl << "Test case 5:-----------------------------------------------------------------------------------"; - - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - - mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; - mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; - mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; - mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5; - std::vector points{ pnt1,pnt2,pnt3,pnt4 }; + /***************************** + * whole pixel (white) + half pixel (gray) in x-direction + * -> mean of 191.5 expected + ******************************/ + MITK_INFO << std::endl << "Test case 5:-----------------------------------------------------------------------------------"; + + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + + mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; + mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; + mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; + mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5; + std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 191.50, 63.50, 128.63499999046327); + this->VerifyStatistics(statisticsObjectTimestep0, 191.50, 89.802561210691536, 128.63499999046327); } void mitkImageStatisticsCalculatorTestSuite::TestCase6() { - /***************************** - * quarter pixel (black) + whole pixel (white) + half pixel (gray) in x-direction - * -> mean of 191.5 expected - ******************************/ - MITK_INFO << std::endl << "Test case 6:-----------------------------------------------------------------------------------"; - - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - - mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; - mitk::Point2D pnt2; pnt2[0] = 9.25; pnt2[1] = 3.5; - mitk::Point2D pnt3; pnt3[0] = 9.25; pnt3[1] = 4.5; - mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5; - std::vector points{ pnt1,pnt2,pnt3,pnt4 }; + /***************************** + * quarter pixel (black) + whole pixel (white) + half pixel (gray) in x-direction + * -> mean of 191.5 expected + ******************************/ + MITK_INFO << std::endl << "Test case 6:-----------------------------------------------------------------------------------"; + + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + + mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; + mitk::Point2D pnt2; pnt2[0] = 9.25; pnt2[1] = 3.5; + mitk::Point2D pnt3; pnt3[0] = 9.25; pnt3[1] = 4.5; + mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5; + std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 63.50, 128.63499999046327); + this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 89.802561210691536, 128.63499999046327); } void mitkImageStatisticsCalculatorTestSuite::TestCase7() { - /***************************** - * half pixel (black) + whole pixel (white) + half pixel (gray) in x-direction - * -> mean of 127.66 expected - ******************************/ - MITK_INFO << std::endl << "Test case 7:-----------------------------------------------------------------------------------"; - - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - - mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; - mitk::Point2D pnt2; pnt2[0] = 9.0; pnt2[1] = 3.5; - mitk::Point2D pnt3; pnt3[0] = 9.0; pnt3[1] = 4.0; - mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.0; - std::vector points{ pnt1,pnt2,pnt3,pnt4 }; + /***************************** + * half pixel (black) + whole pixel (white) + half pixel (gray) in x-direction + * -> mean of 127.66 expected + ******************************/ + MITK_INFO << std::endl << "Test case 7:-----------------------------------------------------------------------------------"; + + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + + mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; + mitk::Point2D pnt2; pnt2[0] = 9.0; pnt2[1] = 3.5; + mitk::Point2D pnt3; pnt3[0] = 9.0; pnt3[1] = 4.0; + mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.0; + std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 104.10358089689113, 128.7750015258789); + this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 127.50032679696680, 128.7750015258789); } void mitkImageStatisticsCalculatorTestSuite::TestCase8() { - /***************************** - * whole pixel (gray) - * -> mean of 128 expected - ******************************/ - MITK_INFO << std::endl << "Test case 8:-----------------------------------------------------------------------------------"; - - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - - mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; - mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 11.5; - mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 11.5; - mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; - std::vector points{ pnt1,pnt2,pnt3,pnt4 }; + /***************************** + * whole pixel (gray) + * -> mean of 128 expected + ******************************/ + MITK_INFO << std::endl << "Test case 8:-----------------------------------------------------------------------------------"; + + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + + mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; + mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 11.5; + mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 11.5; + mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; + std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 128.0, 0.0, 128.0); + this->VerifyStatistics(statisticsObjectTimestep0, 128.0, 0.0, 128.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase9() { - /***************************** - * whole pixel (gray) + half pixel (white) in y-direction - * -> mean of 191.5 expected - ******************************/ - MITK_INFO << std::endl << "Test case 9:-----------------------------------------------------------------------------------"; - - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - - mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; - mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 12.0; - mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 12.0; - mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; - std::vector points{ pnt1,pnt2,pnt3,pnt4 }; + /***************************** + * whole pixel (gray) + half pixel (white) in y-direction + * -> mean of 191.5 expected + ******************************/ + MITK_INFO << std::endl << "Test case 9:-----------------------------------------------------------------------------------"; + + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + + mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; + mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 12.0; + mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 12.0; + mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; + std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 63.50, 128.63499999046327); + this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 89.802561210691536, 128.63499999046327); } void mitkImageStatisticsCalculatorTestSuite::TestCase10() { - /***************************** - * 2 whole pixel (white) + 2 whole pixel (black) in y-direction - * -> mean of 127.66 expected - ******************************/ - MITK_INFO << std::endl << "Test case 10:-----------------------------------------------------------------------------------"; - - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - - mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; - mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 13.5; - mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 13.5; - mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; - std::vector points{ pnt1,pnt2,pnt3,pnt4 }; + /***************************** + * 2 whole pixel (white) + 2 whole pixel (black) in y-direction + * -> mean of 127.66 expected + ******************************/ + MITK_INFO << std::endl << "Test case 10:-----------------------------------------------------------------------------------"; + + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + + mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; + mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 13.5; + mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 13.5; + mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; + std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 104.10358089689113, 128.7750015258789); + this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 127.50032679696680, 128.7750015258789); } void mitkImageStatisticsCalculatorTestSuite::TestCase11() { - /***************************** - * 9 whole pixels (white) + 3 half pixels (white) - * + 3 whole pixel (black) [ + 3 slightly less than half pixels (black)] - * -> mean of 204.0 expected - ******************************/ - MITK_INFO << std::endl << "Test case 11:-----------------------------------------------------------------------------------"; - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - - mitk::Point2D pnt1; pnt1[0] = 0.5; pnt1[1] = 0.5; - mitk::Point2D pnt2; pnt2[0] = 3.5; pnt2[1] = 3.5; - mitk::Point2D pnt3; pnt3[0] = 8.4999; pnt3[1] = 3.5; - mitk::Point2D pnt4; pnt4[0] = 5.4999; pnt4[1] = 0.5; - std::vector points{ pnt1,pnt2,pnt3,pnt4 }; + /***************************** + * 9 whole pixels (white) + 3 half pixels (white) + * + 3 whole pixel (black) [ + 3 slightly less than half pixels (black)] + * -> mean of 204.0 expected + ******************************/ + MITK_INFO << std::endl << "Test case 11:-----------------------------------------------------------------------------------"; + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + + mitk::Point2D pnt1; pnt1[0] = 0.5; pnt1[1] = 0.5; + mitk::Point2D pnt2; pnt2[0] = 3.5; pnt2[1] = 3.5; + mitk::Point2D pnt3; pnt3[0] = 8.4999; pnt3[1] = 3.5; + mitk::Point2D pnt4; pnt4[0] = 5.4999; pnt4[1] = 0.5; + std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 204.0, 102.00, 253.724998474121083); + this->VerifyStatistics(statisticsObjectTimestep0, 204.0, 105.58003057938019, 253.724998474121083); } void mitkImageStatisticsCalculatorTestSuite::TestCase12() { - /***************************** - * half pixel (white) + whole pixel (white) + half pixel (black) - * -> mean of 212.66 expected - ******************************/ - MITK_INFO << std::endl << "Test case 12:-----------------------------------------------------------------------------------"; - - std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); - m_TestImage = mitk::IOUtil::Load(filename); - m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); - - mitk::Point2D pnt1; pnt1[0] = 9.5; pnt1[1] = 0.5; - mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 2.5; - mitk::Point2D pnt3; pnt3[0] = 11.5; pnt3[1] = 2.5; - std::vector points{ pnt1,pnt2,pnt3 }; + /***************************** + * half pixel (white) + whole pixel (white) + half pixel (black) + * -> mean of 212.66 expected + ******************************/ + MITK_INFO << std::endl << "Test case 12:-----------------------------------------------------------------------------------"; + + std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); + m_TestImage = mitk::IOUtil::Load(filename); + m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); + + mitk::Point2D pnt1; pnt1[0] = 9.5; pnt1[1] = 0.5; + mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 2.5; + mitk::Point2D pnt3; pnt3[0] = 11.5; pnt3[1] = 2.5; + std::vector points{ pnt1,pnt2,pnt3 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); - mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); - planFigMaskGen->SetInputImage(m_TestImage); + mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); + planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - this->VerifyStatistics(statisticsObjectTimestep0, 212.666666666666667, 59.8683741404609923, 254.36499786376954); + this->VerifyStatistics(statisticsObjectTimestep0, 212.666666666666667, 73.323484187082443, 254.36499786376954); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedNoMask() { - MITK_INFO << std::endl << "Test Pic3D cropped without mask:-----------------------------------------------------------------------------------"; - - std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); - m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); - //calculated ground truth via script - mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27; - mitk::ImageStatisticsContainer::RealType expected_mean = -564.1481481481481481; - mitk::ImageStatisticsContainer::RealType expected_MPP = 113.66666666666667; + MITK_INFO << std::endl << "Test Pic3D cropped without mask:-----------------------------------------------------------------------------------"; + + std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); + m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); + //calculated ground truth via script + mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27; + mitk::ImageStatisticsContainer::RealType expected_mean = -564.1481481481481481; + mitk::ImageStatisticsContainer::RealType expected_MPP = 113.66666666666667; //mitk::ImageStatisticsContainer::RealType expected_median = -825; - mitk::ImageStatisticsContainer::RealType expected_skewness = 0.7120461106763573; - mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.8794464383714844; - mitk::ImageStatisticsContainer::RealType expected_variance = 140541.38545953357; - mitk::ImageStatisticsContainer::RealType expected_standarddev = 374.88849736892911; - mitk::ImageStatisticsContainer::RealType expected_min = -927; - mitk::ImageStatisticsContainer::RealType expected_max = 147; - mitk::ImageStatisticsContainer::RealType expected_RMS = 677.35110431630551; - mitk::ImageStatisticsContainer::IndexType expected_minIndex; - expected_minIndex.set_size(3); - expected_minIndex[0] = 2; - expected_minIndex[1] = 1; - expected_minIndex[2] = 1; - - mitk::ImageStatisticsContainer::IndexType expected_maxIndex; - expected_maxIndex.set_size(3); - expected_maxIndex[0] = 0; - expected_maxIndex[1] = 1; - expected_maxIndex[2] = 2; - - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage)); - auto statisticsObject = statisticsContainer->GetStatisticsForTimeStep(0); - - VerifyStatistics(statisticsObject, - expected_N, - expected_mean, - expected_MPP, - expected_skewness, - expected_kurtosis, - expected_variance, - expected_standarddev, - expected_min, - expected_max, - expected_RMS, - expected_minIndex, - expected_maxIndex); + mitk::ImageStatisticsContainer::RealType expected_skewness = 0.7120461106763573; + mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.8794464383714844; + mitk::ImageStatisticsContainer::RealType expected_variance = 145946.82336182334; + mitk::ImageStatisticsContainer::RealType expected_standarddev = 382.02987234223366; + mitk::ImageStatisticsContainer::RealType expected_min = -927; + mitk::ImageStatisticsContainer::RealType expected_max = 147; + mitk::ImageStatisticsContainer::RealType expected_RMS = 681.32955052662169; + mitk::ImageStatisticsContainer::IndexType expected_minIndex; + expected_minIndex.set_size(3); + expected_minIndex[0] = 2; + expected_minIndex[1] = 1; + expected_minIndex[2] = 1; + + mitk::ImageStatisticsContainer::IndexType expected_maxIndex; + expected_maxIndex.set_size(3); + expected_maxIndex[0] = 0; + expected_maxIndex[1] = 1; + expected_maxIndex[2] = 2; + + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage)); + auto statisticsObject = statisticsContainer->GetStatisticsForTimeStep(0); + + VerifyStatistics(statisticsObject, + expected_N, + expected_mean, + expected_MPP, + expected_skewness, + expected_kurtosis, + expected_variance, + expected_standarddev, + expected_min, + expected_max, + expected_RMS, + expected_minIndex, + expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedBinMask() { - MITK_INFO << std::endl << "Test Pic3D cropped binary mask:-----------------------------------------------------------------------------------"; - - std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); - m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); - - std::string Pic3DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedBinMask.nrrd"); - m_Pic3DCroppedBinMask = mitk::IOUtil::Load(Pic3DCroppedBinMaskFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_Pic3DCroppedBinMask.IsNotNull()); - //calculated ground truth via script - mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0765697398089618; - mitk::ImageStatisticsContainer::RealType expected_MPP = -nan(""); - mitk::ImageStatisticsContainer::RealType expected_max = -22; - mitk::ImageStatisticsContainer::RealType expected_mean = -464; - mitk::ImageStatisticsContainer::RealType expected_min = -846; - mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4; - mitk::ImageStatisticsContainer::RealType expected_RMS = 595.42631785973322; - mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0544059290851858; - mitk::ImageStatisticsContainer::RealType expected_standarddev = 373.14407405183323; - mitk::ImageStatisticsContainer::RealType expected_variance = 139236.50; - mitk::ImageStatisticsContainer::IndexType expected_minIndex; - expected_minIndex.set_size(3); - expected_minIndex[0] = 1; - expected_minIndex[1] = 0; - expected_minIndex[2] = 0; - - mitk::ImageStatisticsContainer::IndexType expected_maxIndex; - expected_maxIndex.set_size(3); - expected_maxIndex[0] = 0; - expected_maxIndex[1] = 0; - expected_maxIndex[2] = 1; - - mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New(); - imgMaskGen->SetImageMask(m_Pic3DCroppedBinMask); - imgMaskGen->SetInputImage(m_Pic3DCroppedImage); - imgMaskGen->SetTimeStep(0); - - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, imgMaskGen.GetPointer(), nullptr, 1)); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - - VerifyStatistics(statisticsObjectTimestep0, - expected_N, - expected_mean, - expected_MPP, - expected_skewness, - expected_kurtosis, - expected_variance, - expected_standarddev, - expected_min, - expected_max, - expected_RMS, - expected_minIndex, - expected_maxIndex); + MITK_INFO << std::endl << "Test Pic3D cropped binary mask:-----------------------------------------------------------------------------------"; + + std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); + m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); + + std::string Pic3DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedBinMask.nrrd"); + m_Pic3DCroppedBinMask = mitk::IOUtil::Load(Pic3DCroppedBinMaskFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_Pic3DCroppedBinMask.IsNotNull()); + //calculated ground truth via script + mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0765697398089618; + mitk::ImageStatisticsContainer::RealType expected_MPP = -nan(""); + mitk::ImageStatisticsContainer::RealType expected_max = -22; + mitk::ImageStatisticsContainer::RealType expected_mean = -464; + mitk::ImageStatisticsContainer::RealType expected_min = -846; + mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4; + mitk::ImageStatisticsContainer::RealType expected_RMS = 633.20191618998331; + mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0544059290851858; + mitk::ImageStatisticsContainer::RealType expected_standarddev = 430.86966320067910; + mitk::ImageStatisticsContainer::RealType expected_variance = 185648.66666666663; + mitk::ImageStatisticsContainer::IndexType expected_minIndex; + expected_minIndex.set_size(3); + expected_minIndex[0] = 1; + expected_minIndex[1] = 0; + expected_minIndex[2] = 0; + + mitk::ImageStatisticsContainer::IndexType expected_maxIndex; + expected_maxIndex.set_size(3); + expected_maxIndex[0] = 0; + expected_maxIndex[1] = 0; + expected_maxIndex[2] = 1; + + mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New(); + imgMaskGen->SetImageMask(m_Pic3DCroppedBinMask); + imgMaskGen->SetInputImage(m_Pic3DCroppedImage); + imgMaskGen->SetTimeStep(0); + + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, imgMaskGen.GetPointer(), nullptr, 1)); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + + VerifyStatistics(statisticsObjectTimestep0, + expected_N, + expected_mean, + expected_MPP, + expected_skewness, + expected_kurtosis, + expected_variance, + expected_standarddev, + expected_min, + expected_max, + expected_RMS, + expected_minIndex, + expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedMultilabelMask() { - MITK_INFO << std::endl << "Test Pic3D cropped multilabel mask:-----------------------------------------------------------------------------------"; - - std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); - m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); - - std::string Pic3DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedMultilabelMask.nrrd"); - m_Pic3DCroppedMultilabelMask = mitk::IOUtil::Load(Pic3DCroppedMultilabelMaskFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D multilabel mask", m_Pic3DCroppedMultilabelMask.IsNotNull()); - //calculated ground truth via script - mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5; - mitk::ImageStatisticsContainer::RealType expected_MPP = -nan(""); - mitk::ImageStatisticsContainer::RealType expected_max = -22; - mitk::ImageStatisticsContainer::RealType expected_mean = -586.33333333333333; - mitk::ImageStatisticsContainer::RealType expected_min = -916; - mitk::ImageStatisticsContainer::VoxelCountType expected_N = 3; - mitk::ImageStatisticsContainer::RealType expected_RMS = 710.3006405741163; - mitk::ImageStatisticsContainer::RealType expected_skewness = 0.6774469597523700; - mitk::ImageStatisticsContainer::RealType expected_standarddev = 400.92421007245525; - mitk::ImageStatisticsContainer::RealType expected_variance = 160740.22222222222; - mitk::ImageStatisticsContainer::IndexType expected_minIndex; - expected_minIndex.set_size(3); - expected_minIndex[0] = 2; - expected_minIndex[1] = 0; - expected_minIndex[2] = 1; - - mitk::ImageStatisticsContainer::IndexType expected_maxIndex; - expected_maxIndex.set_size(3); - expected_maxIndex[0] = 0; - expected_maxIndex[1] = 0; - expected_maxIndex[2] = 1; - - mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New(); - imgMaskGen->SetImageMask(m_Pic3DCroppedMultilabelMask); - imgMaskGen->SetInputImage(m_Pic3DCroppedImage); - imgMaskGen->SetTimeStep(0); - - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, imgMaskGen.GetPointer(), nullptr, 2)); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - - VerifyStatistics(statisticsObjectTimestep0, - expected_N, - expected_mean, - expected_MPP, - expected_skewness, - expected_kurtosis, - expected_variance, - expected_standarddev, - expected_min, - expected_max, - expected_RMS, - expected_minIndex, - expected_maxIndex); + MITK_INFO << std::endl << "Test Pic3D cropped multilabel mask:-----------------------------------------------------------------------------------"; + + std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); + m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); + + std::string Pic3DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedMultilabelMask.nrrd"); + m_Pic3DCroppedMultilabelMask = mitk::IOUtil::Load(Pic3DCroppedMultilabelMaskFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D multilabel mask", m_Pic3DCroppedMultilabelMask.IsNotNull()); + //calculated ground truth via script + mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5; + mitk::ImageStatisticsContainer::RealType expected_MPP = -nan(""); + mitk::ImageStatisticsContainer::RealType expected_max = -22; + mitk::ImageStatisticsContainer::RealType expected_mean = -586.33333333333333; + mitk::ImageStatisticsContainer::RealType expected_min = -916; + mitk::ImageStatisticsContainer::VoxelCountType expected_N = 3; + mitk::ImageStatisticsContainer::RealType expected_RMS = 764.78566351044469; + mitk::ImageStatisticsContainer::RealType expected_skewness = 0.6774469597523700; + mitk::ImageStatisticsContainer::RealType expected_standarddev = 491.02987010296363; + mitk::ImageStatisticsContainer::RealType expected_variance = 241110.33333333334; + mitk::ImageStatisticsContainer::IndexType expected_minIndex; + expected_minIndex.set_size(3); + expected_minIndex[0] = 2; + expected_minIndex[1] = 0; + expected_minIndex[2] = 1; + + mitk::ImageStatisticsContainer::IndexType expected_maxIndex; + expected_maxIndex.set_size(3); + expected_maxIndex[0] = 0; + expected_maxIndex[1] = 0; + expected_maxIndex[2] = 1; + + mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New(); + imgMaskGen->SetImageMask(m_Pic3DCroppedMultilabelMask); + imgMaskGen->SetInputImage(m_Pic3DCroppedImage); + imgMaskGen->SetTimeStep(0); + + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, imgMaskGen.GetPointer(), nullptr, 2)); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + + VerifyStatistics(statisticsObjectTimestep0, + expected_N, + expected_mean, + expected_MPP, + expected_skewness, + expected_kurtosis, + expected_variance, + expected_standarddev, + expected_min, + expected_max, + expected_RMS, + expected_minIndex, + expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedPlanarFigure() { - MITK_INFO << std::endl << "Test Pic3D cropped planar figure:-----------------------------------------------------------------------------------"; - - std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); - m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); - - std::string Pic3DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedPF.pf"); - m_Pic3DCroppedPlanarFigure = mitk::IOUtil::Load(Pic3DCroppedPlanarFigureFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D planar figure", m_Pic3DCroppedPlanarFigure.IsNotNull()); - //calculated ground truth via script - mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1; - mitk::ImageStatisticsContainer::RealType expected_MPP = -nan(""); - mitk::ImageStatisticsContainer::RealType expected_max = -67; - mitk::ImageStatisticsContainer::RealType expected_mean = -446; - mitk::ImageStatisticsContainer::RealType expected_min = -825; - mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2; - mitk::ImageStatisticsContainer::RealType expected_RMS = 585.28369189650243; - mitk::ImageStatisticsContainer::RealType expected_skewness = 0; - mitk::ImageStatisticsContainer::RealType expected_standarddev = 379; - mitk::ImageStatisticsContainer::RealType expected_variance = 143641; - mitk::ImageStatisticsContainer::IndexType expected_minIndex; - expected_minIndex.set_size(3); - expected_minIndex[0] = 1; - expected_minIndex[1] = 1; - expected_minIndex[2] = 1; - - mitk::ImageStatisticsContainer::IndexType expected_maxIndex; - expected_maxIndex.set_size(3); - expected_maxIndex[0] = 0; - expected_maxIndex[1] = 1; - expected_maxIndex[2] = 1; - - mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New(); - pfMaskGen->SetInputImage(m_Pic3DCroppedImage); - pfMaskGen->SetPlanarFigure(m_Pic3DCroppedPlanarFigure); - - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, pfMaskGen.GetPointer())); - auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); - - VerifyStatistics(statisticsObjectTimestep0, - expected_N, - expected_mean, - expected_MPP, - expected_skewness, - expected_kurtosis, - expected_variance, - expected_standarddev, - expected_min, - expected_max, - expected_RMS, - expected_minIndex, - expected_maxIndex); + MITK_INFO << std::endl << "Test Pic3D cropped planar figure:-----------------------------------------------------------------------------------"; + + std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); + m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); + + std::string Pic3DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedPF.pf"); + m_Pic3DCroppedPlanarFigure = mitk::IOUtil::Load(Pic3DCroppedPlanarFigureFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D planar figure", m_Pic3DCroppedPlanarFigure.IsNotNull()); + //calculated ground truth via script + mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1; + mitk::ImageStatisticsContainer::RealType expected_MPP = -nan(""); + mitk::ImageStatisticsContainer::RealType expected_max = -67; + mitk::ImageStatisticsContainer::RealType expected_mean = -446; + mitk::ImageStatisticsContainer::RealType expected_min = -825; + mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2; + mitk::ImageStatisticsContainer::RealType expected_RMS = 697.27899724572228; + mitk::ImageStatisticsContainer::RealType expected_skewness = 0; + mitk::ImageStatisticsContainer::RealType expected_standarddev = 535.98694013940303; + mitk::ImageStatisticsContainer::RealType expected_variance = 287282.0; + mitk::ImageStatisticsContainer::IndexType expected_minIndex; + expected_minIndex.set_size(3); + expected_minIndex[0] = 1; + expected_minIndex[1] = 1; + expected_minIndex[2] = 1; + + mitk::ImageStatisticsContainer::IndexType expected_maxIndex; + expected_maxIndex.set_size(3); + expected_maxIndex[0] = 0; + expected_maxIndex[1] = 1; + expected_maxIndex[2] = 1; + + mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New(); + pfMaskGen->SetInputImage(m_Pic3DCroppedImage); + pfMaskGen->SetPlanarFigure(m_Pic3DCroppedPlanarFigure); + + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, pfMaskGen.GetPointer())); + auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); + + VerifyStatistics(statisticsObjectTimestep0, + expected_N, + expected_mean, + expected_MPP, + expected_skewness, + expected_kurtosis, + expected_variance, + expected_standarddev, + expected_min, + expected_max, + expected_RMS, + expected_minIndex, + expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedNoMaskTimeStep1() { - MITK_INFO << std::endl << "Test US4D cropped without mask timestep 1:-----------------------------------------------------------------------------------"; + MITK_INFO << std::endl << "Test US4D cropped without mask timestep 1:-----------------------------------------------------------------------------------"; std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); - m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); - //calculated ground truth via script - mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5398359155908228; - mitk::ImageStatisticsContainer::RealType expected_MPP = 157.74074074074073; - mitk::ImageStatisticsContainer::RealType expected_max = 199; - mitk::ImageStatisticsContainer::RealType expected_mean = 157.74074074074073; - mitk::ImageStatisticsContainer::RealType expected_min = 101; - mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27; - mitk::ImageStatisticsContainer::RealType expected_RMS = 160.991718213494823; - mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0347280313508018; - mitk::ImageStatisticsContainer::RealType expected_standarddev = 32.189936997387058; - mitk::ImageStatisticsContainer::RealType expected_variance = 1036.19204389574722; - mitk::ImageStatisticsContainer::IndexType expected_minIndex; - expected_minIndex.set_size(3); - expected_minIndex[0] = 0; - expected_minIndex[1] = 2; - expected_minIndex[2] = 0; - - mitk::ImageStatisticsContainer::IndexType expected_maxIndex; - expected_maxIndex.set_size(3); - expected_maxIndex[0] = 0; - expected_maxIndex[1] = 0; - expected_maxIndex[2] = 1; - - mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New(); - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage)); - auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); - - VerifyStatistics(statisticsObjectTimestep1, - expected_N, - expected_mean, - expected_MPP, - expected_skewness, - expected_kurtosis, - expected_variance, - expected_standarddev, - expected_min, - expected_max, - expected_RMS, - expected_minIndex, - expected_maxIndex); + m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); + //calculated ground truth via script + mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5398359155908228; + mitk::ImageStatisticsContainer::RealType expected_MPP = 157.74074074074073; + mitk::ImageStatisticsContainer::RealType expected_max = 199; + mitk::ImageStatisticsContainer::RealType expected_mean = 157.74074074074073; + mitk::ImageStatisticsContainer::RealType expected_min = 101; + mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27; + mitk::ImageStatisticsContainer::RealType expected_RMS = 161.11544579426010; + mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0347280313508018; + mitk::ImageStatisticsContainer::RealType expected_standarddev = 32.803133753432512; + mitk::ImageStatisticsContainer::RealType expected_variance = 1076.0455840455834; + mitk::ImageStatisticsContainer::IndexType expected_minIndex; + expected_minIndex.set_size(3); + expected_minIndex[0] = 0; + expected_minIndex[1] = 2; + expected_minIndex[2] = 0; + + mitk::ImageStatisticsContainer::IndexType expected_maxIndex; + expected_maxIndex.set_size(3); + expected_maxIndex[0] = 0; + expected_maxIndex[1] = 0; + expected_maxIndex[2] = 1; + + mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New(); + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage)); + auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); + + VerifyStatistics(statisticsObjectTimestep1, + expected_N, + expected_mean, + expected_MPP, + expected_skewness, + expected_kurtosis, + expected_variance, + expected_standarddev, + expected_min, + expected_max, + expected_RMS, + expected_minIndex, + expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedBinMaskTimeStep1() { - MITK_INFO << std::endl << "Test US4D cropped with binary mask timestep 1:-----------------------------------------------------------------------------------"; - - std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); - m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); - - std::string US4DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedBinMask.nrrd"); - m_US4DCroppedBinMask = mitk::IOUtil::Load(US4DCroppedBinMaskFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading US4D binary mask", m_US4DCroppedBinMask.IsNotNull()); - //calculated ground truth via script - mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5863739712889191; - mitk::ImageStatisticsContainer::RealType expected_MPP = 166.75; - mitk::ImageStatisticsContainer::RealType expected_max = 199; - mitk::ImageStatisticsContainer::RealType expected_mean = 166.75; - mitk::ImageStatisticsContainer::RealType expected_min = 120; - mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4; - mitk::ImageStatisticsContainer::RealType expected_RMS = 169.70636405273669; - mitk::ImageStatisticsContainer::RealType expected_skewness = -0.4285540263894276; - mitk::ImageStatisticsContainer::RealType expected_standarddev = 31.538666744172936; - mitk::ImageStatisticsContainer::RealType expected_variance = 994.6874999999999; - mitk::ImageStatisticsContainer::IndexType expected_minIndex; - expected_minIndex.set_size(3); - expected_minIndex[0] = 0; - expected_minIndex[1] = 0; - expected_minIndex[2] = 2; - - mitk::ImageStatisticsContainer::IndexType expected_maxIndex; - expected_maxIndex.set_size(3); - expected_maxIndex[0] = 1; - expected_maxIndex[1] = 1; - expected_maxIndex[2] = 1; - - mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New(); - imgMask1->SetInputImage(m_US4DCroppedImage); - imgMask1->SetImageMask(m_US4DCroppedBinMask); - - mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New(); - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1)); - auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); - - VerifyStatistics(statisticsObjectTimestep1, - expected_N, - expected_mean, - expected_MPP, - expected_skewness, - expected_kurtosis, - expected_variance, - expected_standarddev, - expected_min, - expected_max, - expected_RMS, - expected_minIndex, - expected_maxIndex); + MITK_INFO << std::endl << "Test US4D cropped with binary mask timestep 1:-----------------------------------------------------------------------------------"; + + std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); + m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); + + std::string US4DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedBinMask.nrrd"); + m_US4DCroppedBinMask = mitk::IOUtil::Load(US4DCroppedBinMaskFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading US4D binary mask", m_US4DCroppedBinMask.IsNotNull()); + //calculated ground truth via script + mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5863739712889191; + mitk::ImageStatisticsContainer::RealType expected_MPP = 166.75; + mitk::ImageStatisticsContainer::RealType expected_max = 199; + mitk::ImageStatisticsContainer::RealType expected_mean = 166.75; + mitk::ImageStatisticsContainer::RealType expected_min = 120; + mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4; + mitk::ImageStatisticsContainer::RealType expected_RMS = 170.68043971117487; + mitk::ImageStatisticsContainer::RealType expected_skewness = -0.4285540263894276; + mitk::ImageStatisticsContainer::RealType expected_standarddev = 36.417715469260287; + mitk::ImageStatisticsContainer::RealType expected_variance = 1326.25; + mitk::ImageStatisticsContainer::IndexType expected_minIndex; + expected_minIndex.set_size(3); + expected_minIndex[0] = 0; + expected_minIndex[1] = 0; + expected_minIndex[2] = 2; + + mitk::ImageStatisticsContainer::IndexType expected_maxIndex; + expected_maxIndex.set_size(3); + expected_maxIndex[0] = 1; + expected_maxIndex[1] = 1; + expected_maxIndex[2] = 1; + + mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New(); + imgMask1->SetInputImage(m_US4DCroppedImage); + imgMask1->SetImageMask(m_US4DCroppedBinMask); + + mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New(); + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1)); + auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); + + VerifyStatistics(statisticsObjectTimestep1, + expected_N, + expected_mean, + expected_MPP, + expected_skewness, + expected_kurtosis, + expected_variance, + expected_standarddev, + expected_min, + expected_max, + expected_RMS, + expected_minIndex, + expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedMultilabelMaskTimeStep1() { - MITK_INFO << std::endl << "Test US4D cropped with mulitlabel mask timestep 1:-----------------------------------------------------------------------------------"; - - std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); - m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); - - std::string US4DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedMultilabelMask.nrrd"); - m_US4DCroppedMultilabelMask = mitk::IOUtil::Load(US4DCroppedMultilabelMaskFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading US4D multilabel mask", m_US4DCroppedMultilabelMask.IsNotNull()); - //calculated ground truth via script - mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0432484564918287; - mitk::ImageStatisticsContainer::RealType expected_MPP = 159.75; - mitk::ImageStatisticsContainer::RealType expected_max = 199; - mitk::ImageStatisticsContainer::RealType expected_mean = 159.75; - mitk::ImageStatisticsContainer::RealType expected_min = 120; - mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4; - mitk::ImageStatisticsContainer::RealType expected_RMS = 163.74446555532802; - mitk::ImageStatisticsContainer::RealType expected_skewness = -0.004329226115093; - mitk::ImageStatisticsContainer::RealType expected_standarddev = 35.947009611371016; - mitk::ImageStatisticsContainer::RealType expected_variance = 1292.187500000000227; - mitk::ImageStatisticsContainer::IndexType expected_minIndex; - expected_minIndex.set_size(3); - expected_minIndex[0] = 0; - expected_minIndex[1] = 0; - expected_minIndex[2] = 2; - - mitk::ImageStatisticsContainer::IndexType expected_maxIndex; - expected_maxIndex.set_size(3); - expected_maxIndex[0] = 0; - expected_maxIndex[1] = 0; - expected_maxIndex[2] = 1; - - mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New(); - imgMask1->SetInputImage(m_US4DCroppedImage); - imgMask1->SetImageMask(m_US4DCroppedMultilabelMask); - - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1)); - auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); - - VerifyStatistics(statisticsObjectTimestep1, - expected_N, - expected_mean, - expected_MPP, - expected_skewness, - expected_kurtosis, - expected_variance, - expected_standarddev, - expected_min, - expected_max, - expected_RMS, - expected_minIndex, - expected_maxIndex); + MITK_INFO << std::endl << "Test US4D cropped with mulitlabel mask timestep 1:-----------------------------------------------------------------------------------"; + + std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); + m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); + + std::string US4DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedMultilabelMask.nrrd"); + m_US4DCroppedMultilabelMask = mitk::IOUtil::Load(US4DCroppedMultilabelMaskFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading US4D multilabel mask", m_US4DCroppedMultilabelMask.IsNotNull()); + //calculated ground truth via script + mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0432484564918287; + mitk::ImageStatisticsContainer::RealType expected_MPP = 159.75; + mitk::ImageStatisticsContainer::RealType expected_max = 199; + mitk::ImageStatisticsContainer::RealType expected_mean = 159.75; + mitk::ImageStatisticsContainer::RealType expected_min = 120; + mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4; + mitk::ImageStatisticsContainer::RealType expected_RMS = 165.05447333128134; + mitk::ImageStatisticsContainer::RealType expected_skewness = -0.004329226115093; + mitk::ImageStatisticsContainer::RealType expected_standarddev = 41.508031351374242; + mitk::ImageStatisticsContainer::RealType expected_variance = 1722.9166666666670; + mitk::ImageStatisticsContainer::IndexType expected_minIndex; + expected_minIndex.set_size(3); + expected_minIndex[0] = 0; + expected_minIndex[1] = 0; + expected_minIndex[2] = 2; + + mitk::ImageStatisticsContainer::IndexType expected_maxIndex; + expected_maxIndex.set_size(3); + expected_maxIndex[0] = 0; + expected_maxIndex[1] = 0; + expected_maxIndex[2] = 1; + + mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New(); + imgMask1->SetInputImage(m_US4DCroppedImage); + imgMask1->SetImageMask(m_US4DCroppedMultilabelMask); + + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1)); + auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); + + VerifyStatistics(statisticsObjectTimestep1, + expected_N, + expected_mean, + expected_MPP, + expected_skewness, + expected_kurtosis, + expected_variance, + expected_standarddev, + expected_min, + expected_max, + expected_RMS, + expected_minIndex, + expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedPlanarFigureTimeStep1() { - MITK_INFO << std::endl << "Test US4D cropped planar figure timestep 1:-----------------------------------------------------------------------------------"; - - std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); - m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); - - std::string US4DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedPF.pf"); - m_US4DCroppedPlanarFigure = mitk::IOUtil::Load(US4DCroppedPlanarFigureFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading US4D planar figure", m_US4DCroppedPlanarFigure.IsNotNull()); - //calculated ground truth via script - mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1; - mitk::ImageStatisticsContainer::RealType expected_MPP = 172.5; - mitk::ImageStatisticsContainer::RealType expected_max = 197; - mitk::ImageStatisticsContainer::RealType expected_mean = 172.5; - mitk::ImageStatisticsContainer::RealType expected_min = 148; - mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2; - mitk::ImageStatisticsContainer::RealType expected_RMS = 174.23116827938679; - mitk::ImageStatisticsContainer::RealType expected_skewness = 0; - mitk::ImageStatisticsContainer::RealType expected_standarddev = 24.5; - mitk::ImageStatisticsContainer::RealType expected_variance = 600.25; - mitk::ImageStatisticsContainer::IndexType expected_minIndex; - expected_minIndex.set_size(3); - expected_minIndex[0] = 2; - expected_minIndex[1] = 2; - expected_minIndex[2] = 2; - - mitk::ImageStatisticsContainer::IndexType expected_maxIndex; - expected_maxIndex.set_size(3); - expected_maxIndex[0] = 2; - expected_maxIndex[1] = 2; - expected_maxIndex[2] = 1; - - mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New(); - pfMaskGen->SetInputImage(m_US4DCroppedImage); - pfMaskGen->SetPlanarFigure(m_US4DCroppedPlanarFigure); - - mitk::ImageStatisticsContainer::Pointer statisticsContainer; - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, pfMaskGen.GetPointer())); - auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); - - VerifyStatistics(statisticsObjectTimestep1, - expected_N, - expected_mean, - expected_MPP, - expected_skewness, - expected_kurtosis, - expected_variance, - expected_standarddev, - expected_min, - expected_max, - expected_RMS, - expected_minIndex, - expected_maxIndex); + MITK_INFO << std::endl << "Test US4D cropped planar figure timestep 1:-----------------------------------------------------------------------------------"; + + std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); + m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); + + std::string US4DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedPF.pf"); + m_US4DCroppedPlanarFigure = mitk::IOUtil::Load(US4DCroppedPlanarFigureFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading US4D planar figure", m_US4DCroppedPlanarFigure.IsNotNull()); + //calculated ground truth via script + mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1; + mitk::ImageStatisticsContainer::RealType expected_MPP = 172.5; + mitk::ImageStatisticsContainer::RealType expected_max = 197; + mitk::ImageStatisticsContainer::RealType expected_mean = 172.5; + mitk::ImageStatisticsContainer::RealType expected_min = 148; + mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2; + mitk::ImageStatisticsContainer::RealType expected_RMS = 175.94530400098776; + mitk::ImageStatisticsContainer::RealType expected_skewness = 0; + mitk::ImageStatisticsContainer::RealType expected_standarddev = 34.648232278140831; + mitk::ImageStatisticsContainer::RealType expected_variance = 1200.5000000000002; + mitk::ImageStatisticsContainer::IndexType expected_minIndex; + expected_minIndex.set_size(3); + expected_minIndex[0] = 2; + expected_minIndex[1] = 2; + expected_minIndex[2] = 2; + + mitk::ImageStatisticsContainer::IndexType expected_maxIndex; + expected_maxIndex.set_size(3); + expected_maxIndex[0] = 2; + expected_maxIndex[1] = 2; + expected_maxIndex[2] = 1; + + mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New(); + pfMaskGen->SetInputImage(m_US4DCroppedImage); + pfMaskGen->SetPlanarFigure(m_US4DCroppedPlanarFigure); + + mitk::ImageStatisticsContainer::Pointer statisticsContainer; + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, pfMaskGen.GetPointer())); + auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); + + VerifyStatistics(statisticsObjectTimestep1, + expected_N, + expected_mean, + expected_MPP, + expected_skewness, + expected_kurtosis, + expected_variance, + expected_standarddev, + expected_min, + expected_max, + expected_RMS, + expected_minIndex, + expected_maxIndex); } void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedAllTimesteps() { - MITK_INFO << std::endl << "Test US4D cropped all timesteps:-----------------------------------------------------------------------------------"; - - std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); - m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); - mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New(); - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage)); - for (int i = 0; i < 4; i++) - { - CPPUNIT_ASSERT_MESSAGE("Error computing statistics for multiple timestep", statisticsContainer->TimeStepExists(i)); - } + MITK_INFO << std::endl << "Test US4D cropped all timesteps:-----------------------------------------------------------------------------------"; + + std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); + m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); + mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New(); + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage)); + for (int i = 0; i < 4; i++) + { + CPPUNIT_ASSERT_MESSAGE("Error computing statistics for multiple timestep", statisticsContainer->TimeStepExists(i)); + } } void mitkImageStatisticsCalculatorTestSuite::TestUS4DCropped3DMask() { - MITK_INFO << std::endl << "Test US4D cropped with 3D binary Mask:-----------------------------------------------------------------------------------"; - - std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); - m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); - - std::string US4DCropped3DBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped3DBinMask.nrrd"); - m_US4DCropped3DBinMask = mitk::IOUtil::Load(US4DCropped3DBinMaskFile); - CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_US4DCropped3DBinMask.IsNotNull()); - //calculated ground truth via script - mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1; - mitk::ImageStatisticsContainer::RealType expected_MPP = 198; - mitk::ImageStatisticsContainer::RealType expected_max = 199; - mitk::ImageStatisticsContainer::RealType expected_mean = 198; - mitk::ImageStatisticsContainer::RealType expected_min = 197; - mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2; - mitk::ImageStatisticsContainer::RealType expected_RMS = 198.00252523642217; - mitk::ImageStatisticsContainer::RealType expected_skewness = 0; - mitk::ImageStatisticsContainer::RealType expected_standarddev = 1; - mitk::ImageStatisticsContainer::RealType expected_variance = 1; - mitk::ImageStatisticsContainer::IndexType expected_minIndex; - expected_minIndex.set_size(3); - expected_minIndex[0] = 1; - expected_minIndex[1] = 2; - expected_minIndex[2] = 1; - - mitk::ImageStatisticsContainer::IndexType expected_maxIndex; - expected_maxIndex.set_size(3); - expected_maxIndex[0] = 1; - expected_maxIndex[1] = 1; - expected_maxIndex[2] = 1; - - mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New(); - imgMask1->SetInputImage(m_US4DCroppedImage); - imgMask1->SetImageMask(m_US4DCropped3DBinMask); - - mitk::ImageStatisticsContainer::Pointer statisticsContainer = mitk::ImageStatisticsContainer::New(); - CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1)); - auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); - - VerifyStatistics(statisticsObjectTimestep1, - expected_N, - expected_mean, - expected_MPP, - expected_skewness, - expected_kurtosis, - expected_variance, - expected_standarddev, - expected_min, - expected_max, - expected_RMS, - expected_minIndex, - expected_maxIndex); + MITK_INFO << std::endl << "Test US4D cropped with 3D binary Mask:-----------------------------------------------------------------------------------"; + + std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); + m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); + + std::string US4DCropped3DBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped3DBinMask.nrrd"); + m_US4DCropped3DBinMask = mitk::IOUtil::Load(US4DCropped3DBinMaskFile); + CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_US4DCropped3DBinMask.IsNotNull()); + //calculated ground truth via script + mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1; + mitk::ImageStatisticsContainer::RealType expected_MPP = 198; + mitk::ImageStatisticsContainer::RealType expected_max = 199; + mitk::ImageStatisticsContainer::RealType expected_mean = 198; + mitk::ImageStatisticsContainer::RealType expected_min = 197; + mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2; + mitk::ImageStatisticsContainer::RealType expected_RMS = 198.00505044063902; + mitk::ImageStatisticsContainer::RealType expected_skewness = 0; + mitk::ImageStatisticsContainer::RealType expected_standarddev = 1.4142135623730951; + mitk::ImageStatisticsContainer::RealType expected_variance = 2; + mitk::ImageStatisticsContainer::IndexType expected_minIndex; + expected_minIndex.set_size(3); + expected_minIndex[0] = 1; + expected_minIndex[1] = 2; + expected_minIndex[2] = 1; + + mitk::ImageStatisticsContainer::IndexType expected_maxIndex; + expected_maxIndex.set_size(3); + expected_maxIndex[0] = 1; + expected_maxIndex[1] = 1; + expected_maxIndex[2] = 1; + + mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New(); + imgMask1->SetInputImage(m_US4DCroppedImage); + imgMask1->SetImageMask(m_US4DCropped3DBinMask); + + mitk::ImageStatisticsContainer::Pointer statisticsContainer = mitk::ImageStatisticsContainer::New(); + CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1)); + auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); + + VerifyStatistics(statisticsObjectTimestep1, + expected_N, + expected_mean, + expected_MPP, + expected_skewness, + expected_kurtosis, + expected_variance, + expected_standarddev, + expected_min, + expected_max, + expected_RMS, + expected_minIndex, + expected_maxIndex); } mitk::PlanarPolygon::Pointer mitkImageStatisticsCalculatorTestSuite::GeneratePlanarPolygon(mitk::PlaneGeometry::Pointer geometry, std::vector points) { - mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); - figure->SetPlaneGeometry(geometry); - figure->PlaceFigure(points[0]); + mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); + figure->SetPlaneGeometry(geometry); + figure->PlaceFigure(points[0]); for (unsigned int i = 1; i < points.size(); i++) - { - figure->SetControlPoint(i, points[i], true); - } - return figure; + { + figure->SetControlPoint(i, points[i], true); + } + return figure; } const mitk::ImageStatisticsContainer::Pointer mitkImageStatisticsCalculatorTestSuite::ComputeStatistics(mitk::Image::ConstPointer image, - mitk::MaskGenerator::Pointer maskGen, - mitk::MaskGenerator::Pointer secondardMaskGen, - unsigned short label) + mitk::MaskGenerator::Pointer maskGen, + mitk::MaskGenerator::Pointer secondardMaskGen, + unsigned short label) { - mitk::ImageStatisticsCalculator::Pointer imgStatCalc = mitk::ImageStatisticsCalculator::New(); - imgStatCalc->SetInputImage(image); - - if (maskGen.IsNotNull()) - { - imgStatCalc->SetMask(maskGen.GetPointer()); - if (secondardMaskGen.IsNotNull()) - { - imgStatCalc->SetSecondaryMask(secondardMaskGen.GetPointer()); - } - } - - return imgStatCalc->GetStatistics(label); + mitk::ImageStatisticsCalculator::Pointer imgStatCalc = mitk::ImageStatisticsCalculator::New(); + imgStatCalc->SetInputImage(image); + + if (maskGen.IsNotNull()) + { + imgStatCalc->SetMask(maskGen.GetPointer()); + if (secondardMaskGen.IsNotNull()) + { + imgStatCalc->SetSecondaryMask(secondardMaskGen.GetPointer()); + } + } + + return imgStatCalc->GetStatistics(label); } void mitkImageStatisticsCalculatorTestSuite::VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats, - mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian) + mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian) { mitk::ImageStatisticsContainer::RealType meanObject = 0; mitk::ImageStatisticsContainer::RealType standardDeviationObject = 0; mitk::ImageStatisticsContainer::RealType medianObject = 0; - CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MEAN())); - CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::STANDARDDEVIATION())); - CPPUNIT_ASSERT_NO_THROW(medianObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MEDIAN())); - CPPUNIT_ASSERT_MESSAGE("Calculated mean grayvalue is not equal to the desired value.", std::abs(meanObject - testMean) < mitk::eps); - CPPUNIT_ASSERT_MESSAGE("Calculated grayvalue sd is not equal to the desired value.", std::abs(standardDeviationObject - testSD) < mitk::eps); - CPPUNIT_ASSERT_MESSAGE("Calculated median grayvalue is not equal to the desired value.", std::abs(medianObject - testMedian) < mitk::eps); + CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MEAN())); + CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::STANDARDDEVIATION())); + CPPUNIT_ASSERT_NO_THROW(medianObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MEDIAN())); + CPPUNIT_ASSERT_MESSAGE("Calculated mean grayvalue is not equal to the desired value.", std::abs(meanObject - testMean) < mitk::eps); + CPPUNIT_ASSERT_MESSAGE("Calculated grayvalue sd is not equal to the desired value.", std::abs(standardDeviationObject - testSD) < mitk::eps); + CPPUNIT_ASSERT_MESSAGE("Calculated median grayvalue is not equal to the desired value.", std::abs(medianObject - testMedian) < mitk::eps); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats, - mitk::ImageStatisticsContainer::VoxelCountType N, - mitk::ImageStatisticsContainer::RealType mean, - mitk::ImageStatisticsContainer::RealType MPP, - mitk::ImageStatisticsContainer::RealType skewness, - mitk::ImageStatisticsContainer::RealType kurtosis, - mitk::ImageStatisticsContainer::RealType variance, - mitk::ImageStatisticsContainer::RealType stdev, - mitk::ImageStatisticsContainer::RealType min, - mitk::ImageStatisticsContainer::RealType max, - mitk::ImageStatisticsContainer::RealType RMS, - mitk::ImageStatisticsContainer::IndexType minIndex, - mitk::ImageStatisticsContainer::IndexType maxIndex) + mitk::ImageStatisticsContainer::VoxelCountType N, + mitk::ImageStatisticsContainer::RealType mean, + mitk::ImageStatisticsContainer::RealType MPP, + mitk::ImageStatisticsContainer::RealType skewness, + mitk::ImageStatisticsContainer::RealType kurtosis, + mitk::ImageStatisticsContainer::RealType variance, + mitk::ImageStatisticsContainer::RealType stdev, + mitk::ImageStatisticsContainer::RealType min, + mitk::ImageStatisticsContainer::RealType max, + mitk::ImageStatisticsContainer::RealType RMS, + mitk::ImageStatisticsContainer::IndexType minIndex, + mitk::ImageStatisticsContainer::IndexType maxIndex) { mitk::ImageStatisticsContainer::VoxelCountType numberOfVoxelsObject; mitk::ImageStatisticsContainer::RealType meanObject = 0; mitk::ImageStatisticsContainer::RealType mppObject = 0; mitk::ImageStatisticsContainer::RealType skewnessObject = 0; mitk::ImageStatisticsContainer::RealType kurtosisObject = 0; mitk::ImageStatisticsContainer::RealType varianceObject = 0; mitk::ImageStatisticsContainer::RealType standardDeviationObject = 0; mitk::ImageStatisticsContainer::RealType minObject = 0; mitk::ImageStatisticsContainer::RealType maxObject = 0; mitk::ImageStatisticsContainer::RealType rmsObject = 0; mitk::ImageStatisticsContainer::IndexType minIndexObject(3,0); mitk::ImageStatisticsContainer::IndexType maxIndexObject(3,0); - CPPUNIT_ASSERT_NO_THROW(numberOfVoxelsObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::NUMBEROFVOXELS())); - CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MEAN())); - CPPUNIT_ASSERT_NO_THROW(mppObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MPP())); - CPPUNIT_ASSERT_NO_THROW(skewnessObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::SKEWNESS())); - CPPUNIT_ASSERT_NO_THROW(kurtosisObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::KURTOSIS())); - CPPUNIT_ASSERT_NO_THROW(varianceObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::VARIANCE())); - CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::STANDARDDEVIATION())); - CPPUNIT_ASSERT_NO_THROW(minObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MINIMUM())); - CPPUNIT_ASSERT_NO_THROW(maxObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MAXIMUM())); - CPPUNIT_ASSERT_NO_THROW(rmsObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::RMS())); - CPPUNIT_ASSERT_NO_THROW(minIndexObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MINIMUMPOSITION())); - CPPUNIT_ASSERT_NO_THROW(maxIndexObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MAXIMUMPOSITION())); - - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", numberOfVoxelsObject - N == 0); - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(meanObject - mean) < mitk::eps); - // in three test cases MPP is None because the roi has no positive pixels - if (!std::isnan(mppObject)) - { - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(mppObject - MPP) < mitk::eps); - } - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(skewnessObject - skewness) < mitk::eps); - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(kurtosisObject - kurtosis) < mitk::eps); - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(varianceObject - variance) < mitk::eps); - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(standardDeviationObject - stdev) < mitk::eps); - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minObject - min) < mitk::eps); - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxObject - max) < mitk::eps); - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(rmsObject - RMS) < mitk::eps); - for (unsigned int i = 0; i < minIndex.size(); ++i) - { - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minIndexObject[i] - minIndex[i]) < mitk::eps); - } - for (unsigned int i = 0; i < maxIndex.size(); ++i) - { - CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxIndexObject[i] - maxIndex[i]) < mitk::eps); - } + CPPUNIT_ASSERT_NO_THROW(numberOfVoxelsObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::NUMBEROFVOXELS())); + CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MEAN())); + CPPUNIT_ASSERT_NO_THROW(mppObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MPP())); + CPPUNIT_ASSERT_NO_THROW(skewnessObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::SKEWNESS())); + CPPUNIT_ASSERT_NO_THROW(kurtosisObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::KURTOSIS())); + CPPUNIT_ASSERT_NO_THROW(varianceObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::VARIANCE())); + if (std::abs(varianceObject - variance) >= mitk::eps) + MITK_INFO << "xxx"; + CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::STANDARDDEVIATION())); + CPPUNIT_ASSERT_NO_THROW(minObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MINIMUM())); + CPPUNIT_ASSERT_NO_THROW(maxObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MAXIMUM())); + CPPUNIT_ASSERT_NO_THROW(rmsObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::RMS())); + CPPUNIT_ASSERT_NO_THROW(minIndexObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MINIMUMPOSITION())); + CPPUNIT_ASSERT_NO_THROW(maxIndexObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MAXIMUMPOSITION())); + + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", numberOfVoxelsObject - N == 0); + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(meanObject - mean) < mitk::eps); + // in three test cases MPP is None because the roi has no positive pixels + if (!std::isnan(mppObject)) + { + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(mppObject - MPP) < mitk::eps); + } + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(skewnessObject - skewness) < mitk::eps); + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(kurtosisObject - kurtosis) < mitk::eps); + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(varianceObject - variance) < mitk::eps); + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(standardDeviationObject - stdev) < mitk::eps); + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minObject - min) < mitk::eps); + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxObject - max) < mitk::eps); + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(rmsObject - RMS) < mitk::eps); + for (unsigned int i = 0; i < minIndex.size(); ++i) + { + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minIndexObject[i] - minIndex[i]) < mitk::eps); + } + for (unsigned int i = 0; i < maxIndex.size(); ++i) + { + CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxIndexObject[i] - maxIndex[i]) < mitk::eps); + } } MITK_TEST_SUITE_REGISTRATION(mitkImageStatisticsCalculator) diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp index f55425c872..f517de9074 100644 --- a/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp +++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp @@ -1,230 +1,239 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTestingMacros.h" #include "itkImage.h" -#include "mitkExtendedLabelStatisticsImageFilter.h" -#include "mitkExtendedStatisticsImageFilter.h" +#include "mitkLabelStatisticsImageFilter.h" +#include "mitkStatisticsImageFilter.h" #include "mitkNumericConstants.h" /** \section Testing of Skewness and Kurtosis * This test class is for testing the added coefficients Skewness and Kurtosis * for the mitkExtendedLabelStatisticsImageFilter (Masked Images) and for the * mitkExtendedStatisticsImageFilter (Unmasked Images). Both filter will be tested * against two pictures. */ class mitkImageStatisticsTextureAnalysisTestClass { /** * \brief Explanation of the mitkImageStatisticsTextureAnalysisTestClass test class * * this test class produce test images and masking images with the method CreatingTestImageForDifferentLabelSize. * TestInstanceFortheMaskedStatisticsFilter and TestInstanceFortheUnmaskedStatisticsFilter are the two Instances * for the filters of masking and unmasking images. * TestofSkewnessAndKurtosisForMaskedImagesand TestofSkewnessAndKurtosisForUnmaskedImages are the correlated test * for the checking the values. */ public: - typedef itk::Image< int,3 >ImageType; + typedef itk::Image< mitk::Label::PixelType,3 >ImageType; typedef ImageType::Pointer PointerOfImage; - typedef itk::ExtendedLabelStatisticsImageFilter< ImageType, ImageType > LabelStatisticsFilterType; + typedef mitk::LabelStatisticsImageFilter< ImageType > LabelStatisticsFilterType; typedef LabelStatisticsFilterType::Pointer labelStatisticsFilterPointer; - typedef itk::ExtendedStatisticsImageFilter< ImageType > StatisticsFilterType; + typedef mitk::StatisticsImageFilter< ImageType > StatisticsFilterType; typedef StatisticsFilterType::Pointer StatisticsFilterPointer; ImageType::Pointer CreatingTestImageForDifferentLabelSize( int factorOfDividingThePicture, int bufferValue, int labelValue) { ImageType::Pointer image = ImageType :: New(); ImageType::IndexType start; ImageType::SizeType size; start[0] = 0; start[1] = 0; start[2] = 0; size[0] = 100; size[1] = 100; size[2] = 100; ImageType:: RegionType region; region.SetSize( size ); region.SetIndex( start ); image->SetRegions(region); image->Allocate(); image->FillBuffer(bufferValue); for(unsigned int r = 0; r < 50; r++) { for(int c = 0; c < factorOfDividingThePicture; c++) { for(unsigned int l = 0; l < 100; l++) { ImageType::IndexType pixelIndex; pixelIndex[0] = r; pixelIndex[1] = c; pixelIndex[2] = l; image->SetPixel(pixelIndex, labelValue); } } } return image; } LabelStatisticsFilterType::Pointer TestInstanceFortheMaskedStatisticsFilter(ImageType::Pointer image, ImageType::Pointer maskImage) { LabelStatisticsFilterType::Pointer labelStatisticsFilter; labelStatisticsFilter = LabelStatisticsFilterType::New(); labelStatisticsFilter->SetInput( image ); - labelStatisticsFilter->UseHistogramsOn(); - labelStatisticsFilter->SetHistogramParameters( 20, -10, 10); + + std::unordered_map sizes; + sizes[1] = 20; + + std::unordered_map lowerBounds; + lowerBounds[1] = -10; + + std::unordered_map upperBounds; + upperBounds[1] = 10; + + labelStatisticsFilter->SetHistogramParameters(sizes, lowerBounds, upperBounds); labelStatisticsFilter->SetLabelInput( maskImage ); labelStatisticsFilter->Update(); return labelStatisticsFilter; } StatisticsFilterType::Pointer TestInstanceFortheUnmaskedStatisticsFilter(ImageType::Pointer image ) { StatisticsFilterType::Pointer StatisticsFilter; StatisticsFilter = StatisticsFilterType::New(); StatisticsFilter->SetInput( image ); StatisticsFilter->SetHistogramParameters( 20, -10, 10 ); StatisticsFilter->Update(); return StatisticsFilter; } //test for Skewness,Kurtosis and MPP for masked Images void TestofSkewnessKurtosisAndMPPForMaskedImages(LabelStatisticsFilterType::Pointer labelStatisticsFilter, double expectedSkewness, double expectedKurtosis, double expectedMPP) { // let's create an object of our class bool isSkewsnessLowerlimitCorrect = labelStatisticsFilter->GetSkewness( 1 )- expectedKurtosis+ std::pow(10,-3) <= expectedSkewness; bool isSkewsnessUpperlimitCorrect = labelStatisticsFilter->GetSkewness( 1 )+ expectedKurtosis+ std::pow(10,-3) >= expectedSkewness; MITK_TEST_CONDITION( isSkewsnessLowerlimitCorrect && isSkewsnessUpperlimitCorrect,"expectedSkewness: " << expectedSkewness << " actual Value: " << labelStatisticsFilter->GetSkewness( 1 ) ); bool isKurtosisUpperlimitCorrect = labelStatisticsFilter->GetKurtosis( 1 ) <= expectedKurtosis+ std::pow(10,-3); bool isKurtosisLowerlimitCorrect = expectedKurtosis- std::pow(10,-3) <= labelStatisticsFilter->GetKurtosis( 1 ); MITK_TEST_CONDITION( isKurtosisUpperlimitCorrect && isKurtosisLowerlimitCorrect,"expectedKurtosis: " << expectedKurtosis << " actual Value: " << labelStatisticsFilter->GetKurtosis( 1 ) ); MITK_TEST_CONDITION( ( expectedMPP - labelStatisticsFilter->GetMPP( 1 ) ) < 1, "expected MPP: " << expectedMPP << " actual Value: " << labelStatisticsFilter->GetMPP( 1 ) ); } //test for Entropy,Uniformity and UPP for masked Images void TestofEntropyUniformityAndUppForMaskedImages(LabelStatisticsFilterType::Pointer labelStatisticsFilter, double expectedEntropy, double expectedUniformity, double expectedUPP) { bool calculatedEntropyLowerLimit = labelStatisticsFilter->GetEntropy( 1 ) >= expectedEntropy - std::pow(10,-3); bool calculatedUniformityLowerLimit = labelStatisticsFilter->GetUniformity( 1 ) >= expectedUniformity - std::pow(10,-3); bool calculatedUppLowerLimit = labelStatisticsFilter->GetUPP( 1 ) >= expectedUPP - std::pow(10,-3); bool calculatedEntropyUpperLimit = labelStatisticsFilter->GetEntropy( 1 ) <= expectedEntropy + std::pow(10,-3); bool calculatedUniformityUpperLimit = labelStatisticsFilter->GetUniformity( 1 ) <= expectedUniformity + std::pow(10,-3); bool calculatedUppUpperLimit = labelStatisticsFilter->GetUPP( 1 ) <= expectedUPP + std::pow(10,-3); MITK_TEST_CONDITION( calculatedEntropyLowerLimit && calculatedEntropyUpperLimit, "expected Entropy: " << expectedEntropy << " actual Value: " << labelStatisticsFilter->GetEntropy( 1 ) ); MITK_TEST_CONDITION( calculatedUniformityLowerLimit && calculatedUniformityUpperLimit, "expected Uniformity: " << expectedUniformity << " actual Value: " << labelStatisticsFilter->GetUniformity( 1 ) ); MITK_TEST_CONDITION( calculatedUppLowerLimit && calculatedUppUpperLimit, "expected UPP: " << expectedUPP << " actual Value: " << labelStatisticsFilter->GetUPP( 1 ) ); } //test for Skewness,Kurtosis and MPP for unmasked Images void TestofSkewnessKurtosisAndMPPForUnmaskedImages(StatisticsFilterType::Pointer StatisticsFilter, double expectedSkewness, double expectedKurtosis, double expectedMPP) { // let's create an object of our class bool isSkewsnessLowerlimitCorrect = StatisticsFilter->GetSkewness()- expectedKurtosis+ std::pow(10,-3) <= expectedSkewness; bool isSkewsnessUpperlimitCorrect = StatisticsFilter->GetSkewness()+ expectedKurtosis+ std::pow(10,-3) >= expectedSkewness; MITK_TEST_CONDITION( isSkewsnessLowerlimitCorrect && isSkewsnessUpperlimitCorrect,"expectedSkewness: " << expectedSkewness << " actual Value: " << StatisticsFilter->GetSkewness() ); bool isKurtosisUpperlimitCorrect = StatisticsFilter->GetKurtosis() <= expectedKurtosis+ std::pow(10,-3); bool isKurtosisLowerlimitCorrect = expectedKurtosis- std::pow(10,-3) <= StatisticsFilter->GetKurtosis(); MITK_TEST_CONDITION( isKurtosisUpperlimitCorrect && isKurtosisLowerlimitCorrect,"expectedKurtosis: " << expectedKurtosis << " actual Value: " << StatisticsFilter->GetKurtosis() ); MITK_TEST_CONDITION( ( expectedMPP - StatisticsFilter->GetMPP() ) < mitk::eps, "expected MPP: " << expectedMPP << " actual Value: " << StatisticsFilter->GetMPP() ); } //test for Entropy,Uniformity and UPP for unmasked Images void TestofEntropyUniformityAndUppForUnmaskedImages(StatisticsFilterType::Pointer StatisticsFilter, double expectedEntropy, double expectedUniformity, double expectedUPP) { bool calculatedEntropyLowerLimit = StatisticsFilter->GetEntropy() >= expectedEntropy - std::pow(10,-3); bool calculatedUniformityLowerLimit = StatisticsFilter->GetUniformity() >= expectedUniformity - std::pow(10,-3); bool calculatedUppLowerLimit = StatisticsFilter->GetUPP() >= expectedUPP - std::pow(10,-3); bool calculatedEntropyUpperLimit = StatisticsFilter->GetEntropy() <= expectedEntropy + std::pow(10,-3); bool calculatedUniformityUpperLimit = StatisticsFilter->GetUniformity() <= expectedUniformity + std::pow(10,-3); bool calculatedUppUpperLimit = StatisticsFilter->GetUPP() <= expectedUPP + std::pow(10,-3); MITK_TEST_CONDITION( calculatedEntropyLowerLimit && calculatedEntropyUpperLimit, "expected Entropy: " << expectedEntropy << " actual Value: " << StatisticsFilter->GetEntropy() ); MITK_TEST_CONDITION( calculatedUniformityLowerLimit && calculatedUniformityUpperLimit, "expected Uniformity: " << expectedUniformity << " actual Value: " << StatisticsFilter->GetUniformity() ); MITK_TEST_CONDITION( calculatedUppLowerLimit && calculatedUppUpperLimit, "expected UPP: " << expectedUPP << " actual Value: " << StatisticsFilter->GetUPP() ); } }; int mitkImageStatisticsTextureAnalysisTest(int, char* []) { // always start with this! MITK_TEST_BEGIN("mitkImageStatisticsTextureAnalysisTest") mitkImageStatisticsTextureAnalysisTestClass testclassInstance; mitkImageStatisticsTextureAnalysisTestClass::PointerOfImage labelImage = testclassInstance.CreatingTestImageForDifferentLabelSize(100, 1, 1); mitkImageStatisticsTextureAnalysisTestClass::PointerOfImage image = testclassInstance.CreatingTestImageForDifferentLabelSize(100, 3, 2); mitkImageStatisticsTextureAnalysisTestClass::PointerOfImage image2 = testclassInstance.CreatingTestImageForDifferentLabelSize(50, 3, 2); //test for masked images mitkImageStatisticsTextureAnalysisTestClass::labelStatisticsFilterPointer mitkLabelFilter= testclassInstance.TestInstanceFortheMaskedStatisticsFilter( image,labelImage); testclassInstance.TestofSkewnessKurtosisAndMPPForMaskedImages(mitkLabelFilter, 0, 0.999998, 2.5); testclassInstance.TestofEntropyUniformityAndUppForMaskedImages(mitkLabelFilter, 1, 0.5, 0.5); mitkImageStatisticsTextureAnalysisTestClass::labelStatisticsFilterPointer mitkLabelFilter2= testclassInstance.TestInstanceFortheMaskedStatisticsFilter( image2,labelImage); testclassInstance.TestofSkewnessKurtosisAndMPPForMaskedImages(mitkLabelFilter2, -1.1547, 2.33333, 2.75); testclassInstance.TestofEntropyUniformityAndUppForMaskedImages(mitkLabelFilter2, 0.811278, 0.625, 0.625); //test for unmasked images mitkImageStatisticsTextureAnalysisTestClass::StatisticsFilterPointer mitkFilter= testclassInstance.TestInstanceFortheUnmaskedStatisticsFilter( image); testclassInstance.TestofSkewnessKurtosisAndMPPForUnmaskedImages(mitkFilter, 0, 0.999998, 2.5); testclassInstance.TestofEntropyUniformityAndUppForUnmaskedImages(mitkFilter, 1, 0.5, 0.5); mitkImageStatisticsTextureAnalysisTestClass::StatisticsFilterPointer mitkFilter2= testclassInstance.TestInstanceFortheUnmaskedStatisticsFilter( image2); testclassInstance.TestofSkewnessKurtosisAndMPPForUnmaskedImages(mitkFilter2, -1.1547, 2.33333, 2.75); testclassInstance.TestofEntropyUniformityAndUppForUnmaskedImages( mitkFilter2, 0.811278, 0.625, 0.625); MITK_TEST_END() } diff --git a/Modules/ImageStatistics/files.cmake b/Modules/ImageStatistics/files.cmake index 454e2941ce..3d89557ffe 100644 --- a/Modules/ImageStatistics/files.cmake +++ b/Modules/ImageStatistics/files.cmake @@ -1,50 +1,50 @@ set(CPP_FILES mitkImageStatisticsCalculator.cpp mitkImageStatisticsContainer.cpp mitkPointSetStatisticsCalculator.cpp mitkPointSetDifferenceStatisticsCalculator.cpp mitkIntensityProfile.cpp mitkHotspotMaskGenerator.cpp mitkMaskGenerator.cpp mitkPlanarFigureMaskGenerator.cpp mitkMultiLabelMaskGenerator.cpp mitkImageMaskGenerator.cpp mitkHistogramStatisticsCalculator.cpp mitkIgnorePixelMaskGenerator.cpp mitkImageStatisticsPredicateHelper.cpp mitkImageStatisticsContainerNodeHelper.cpp mitkImageStatisticsContainerManager.cpp mitkStatisticsToImageRelationRule.cpp mitkStatisticsToMaskRelationRule.cpp mitkImageStatisticsConstants.cpp ) set(H_FILES mitkImageStatisticsCalculator.h mitkImageStatisticsContainer.h mitkPointSetDifferenceStatisticsCalculator.h mitkPointSetStatisticsCalculator.h - mitkExtendedStatisticsImageFilter.h - mitkExtendedLabelStatisticsImageFilter.h + mitkStatisticsImageFilter.h + mitkLabelStatisticsImageFilter.h mitkHotspotMaskGenerator.h mitkMaskGenerator.h mitkPlanarFigureMaskGenerator.h mitkMultiLabelMaskGenerator.h mitkImageMaskGenerator.h mitkHistogramStatisticsCalculator.h mitkMaskUtilities.h mitkitkMaskImageFilter.h mitkIgnorePixelMaskGenerator.h mitkMinMaxImageFilterWithIndex.h mitkMinMaxLabelmageFilterWithIndex.h mitkImageStatisticsPredicateHelper.h mitkImageStatisticsContainerNodeHelper.h mitkImageStatisticsContainerManager.h mitkStatisticsToImageRelationRule.h mitkStatisticsToMaskRelationRule.h mitkImageStatisticsConstants.h ) set(TPP_FILES mitkMaskUtilities.tpp -) \ No newline at end of file +) diff --git a/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx b/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx index d301d0d974..706ebb35c1 100644 --- a/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx +++ b/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx @@ -1,1003 +1,998 @@ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ /*========================================================================= * * Portions of this file are subject to the VTK Toolkit Version 3 copyright. * * Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen * * For complete copyright, license and disclaimer of warranty information * please refer to the NOTICE file at the top of the ITK source tree. * *=========================================================================*/ #ifndef __itkMultiGaussianImageSource_hxx #define __itkMultiGaussianImageSource_hxx #include #include #include #include "itkMultiGaussianImageSource.h" #include "itkImageRegionIterator.h" #include "itkObjectFactory.h" #include "itkProgressReporter.h" #include "itkDOMNodeXMLWriter.h" #include namespace itk { /** * */ template< class TOutputImage > MultiGaussianImageSource< TOutputImage > ::MultiGaussianImageSource() { //Initial image is 100 wide in each direction. for ( unsigned int i = 0; i < TOutputImage::GetImageDimension(); i++ ) { m_Size[i] = 100; m_Spacing[i] = 1.0; m_Origin[i] = 0.0; m_SphereMidpoint[i] = 0; } m_NumberOfGaussians = 0; m_Radius = 1; m_RadiusStepNumber = 5; m_MeanValue = 0; m_Min = NumericTraits< OutputImagePixelType >::NonpositiveMin(); m_Max = NumericTraits< OutputImagePixelType >::max(); } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > MultiGaussianImageSource< TOutputImage > ::~MultiGaussianImageSource() {} //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetSize(SizeValueArrayType sizeArray) { const unsigned int count = TOutputImage::ImageDimension; unsigned int i; for ( i = 0; i < count; i++ ) { if ( sizeArray[i] != this->m_Size[i] ) { break; } } if ( i < count ) { this->Modified(); for ( i = 0; i < count; i++ ) { this->m_Size[i] = sizeArray[i]; } } } template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::SizeValueType * MultiGaussianImageSource< TOutputImage > ::GetSize() const { return this->m_Size.GetSize(); } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetSpacing(SpacingValueArrayType spacingArray) { const unsigned int count = TOutputImage::ImageDimension; unsigned int i; for ( i = 0; i < count; i++ ) { if ( spacingArray[i] != this->m_Spacing[i] ) { break; } } if ( i < count ) { this->Modified(); for ( i = 0; i < count; i++ ) { this->m_Spacing[i] = spacingArray[i]; } } } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetOrigin(PointValueArrayType originArray) { const unsigned int count = TOutputImage::ImageDimension; unsigned int i; for ( i = 0; i < count; i++ ) { if ( originArray[i] != this->m_Origin[i] ) { break; } } if ( i < count ) { this->Modified(); for ( i = 0; i < count; i++ ) { this->m_Origin[i] = originArray[i]; } } } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::PointValueType * MultiGaussianImageSource< TOutputImage > ::GetOrigin() const { for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ ) { this->m_OriginArray[i] = this->m_Origin[i]; } return this->m_OriginArray; } template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::SpacingValueType * MultiGaussianImageSource< TOutputImage > ::GetSpacing() const { for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ ) { this->m_SpacingArray[i] = this->m_Spacing[i]; } return this->m_SpacingArray; } //----------------------------------------------------------------------------------------------------------------------- /** * */ template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Max: " << static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( m_Max ) << std::endl; os << indent << "Min: " << static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( m_Min ) << std::endl; os << indent << "Origin: ["; unsigned int ii = 0; while( ii < TOutputImage::ImageDimension - 1 ) { os << m_Origin[ii] << ", "; ++ii; } os << m_Origin[ii] << "]" << std::endl; os << indent << "Spacing: ["; ii = 0; while( ii < TOutputImage::ImageDimension - 1 ) { os << m_Spacing[ii] << ", "; ++ii; } os << m_Spacing[ii] << "]" << std::endl; os << indent << "Size: ["; ii = 0; while( ii < TOutputImage::ImageDimension - 1 ) { os << m_Size[ii] << ", "; ++ii; } os << m_Size[ii] << "]" << std::endl; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > unsigned int MultiGaussianImageSource< TOutputImage > ::GetNumberOfGaussians() const { return this->m_NumberOfGaussians; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > typename MultiGaussianImageSource< TOutputImage >::RadiusType MultiGaussianImageSource< TOutputImage > ::GetRadius() const { return this->m_Radius; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetRadius( RadiusType radius ) { this->m_Radius = radius; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetNumberOfGausssians( unsigned int n ) { this->m_NumberOfGaussians = n; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetRegionOfInterest( ItkVectorType roiMin, ItkVectorType roiMax ) { m_RegionOfInterestMax = roiMax; m_RegionOfInterestMin = roiMin; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType MultiGaussianImageSource< TOutputImage > ::GetMaxMeanValue() const { return m_MeanValue; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType MultiGaussianImageSource< TOutputImage > ::GetMaxValueInSphere() const { return m_MaxValueInSphere; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::IndexType MultiGaussianImageSource< TOutputImage > ::GetMaxValueIndexInSphere() const { return m_MaxValueIndexInSphere; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType MultiGaussianImageSource< TOutputImage > ::GetMinValueInSphere() const { return m_MinValueInSphere; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::IndexType MultiGaussianImageSource< TOutputImage > ::GetMinValueIndexInSphere() const { return m_MinValueIndexInSphere; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::IndexType MultiGaussianImageSource< TOutputImage > ::GetSphereMidpoint() const { return m_SphereMidpoint; } //----------------------------------------------------------------------------------------------------------------------- /* Calculate and return value of the integral of the gaussian in a cuboid region with the dimension 3: in the x-axis between xMin and xMax and in the y-axis between yMin and yMax and in the z-axis also between zMin and zMax. */ template< class TOutputImage > double MultiGaussianImageSource< TOutputImage > ::MultiGaussianFunctionValueAtCuboid(double xMin, double xMax, double yMin, double yMax, double zMin, double zMax) { double mean = 0; double summand0, summand1, summand2, value, factor; for(unsigned int n = 0; n < m_NumberOfGaussians; ++n) { summand0 = FunctionPhi((xMax - m_CenterX[n]) / m_SigmaX[n] ) - FunctionPhi((xMin - m_CenterX[n]) / m_SigmaX[n] ); summand1 = FunctionPhi((yMax - m_CenterY[n]) / m_SigmaY[n] ) - FunctionPhi((yMin - m_CenterY[n]) / m_SigmaY[n] ); summand2 = FunctionPhi((zMax - m_CenterZ[n]) / m_SigmaZ[n] ) - FunctionPhi((zMin - m_CenterZ[n]) / m_SigmaZ[n] ); value = summand0 * summand1 * summand2; factor = (m_SigmaX[n] * m_SigmaY[n] * m_SigmaZ[n] ) * pow(2.0 * itk::Math::pi, 1.5 ); mean = mean + factor * value * m_Altitude[n]; } return mean; } //--------------------------------------------------------------------------------------------------------------------- /* Returns the linear interpolation of the values of the standard normal distribution function. This values could be seen in the vector m_NormalDistValues. */ template< class TOutputImage > double MultiGaussianImageSource< TOutputImage > ::FunctionPhi(double value) { double phiAtValue; //linear interpolation between the values int indexValue = static_cast( 100 * value); if( indexValue > 409 ) { return phiAtValue = 1.0; } else if( indexValue < -409 ) { return phiAtValue = 0.0; } else if( indexValue == 409 ) { return phiAtValue = m_NormalDistValues[409]; } else if( indexValue == -409 ) { return phiAtValue = 1.0 - m_NormalDistValues[409]; } else { bool negative = false; if (indexValue < 0.0) { negative = true; value = -value; } int indexUp = static_cast( 100 * value) + 1; int indexDown = static_cast( 100 * value); double alpha = 100.0 * value - static_cast(indexDown); phiAtValue = (1.0 - alpha) * m_NormalDistValues[indexDown] + alpha * m_NormalDistValues[indexUp] ; if(negative) { phiAtValue = 1.0 - phiAtValue; } return phiAtValue; } } //---------------------------------------------------------------------------------------------------------------------- /* Set the midpoint of the cuboid in a vector m_Midpoints. This cuboids discretise the sphere with the octree method. Set the radius of the cuboid ( = 0.5 * length of the side of the cuboid ) in the vector m_RadiusCuboid. */ template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::InsertPoints( PointType globalCoordinateMidpointCuboid, double cuboidRadius) { typename MapContainerPoints::ElementIdentifier id = m_Midpoints.Size(); m_Midpoints.InsertElement(id, globalCoordinateMidpointCuboid); m_RadiusCuboid.InsertElement(id, cuboidRadius); } //---------------------------------------------------------------------------------------------------------------------- /* This recursive method realise the octree method. It subdivide a cuboid in eight cuboids, when this cuboid crosses the boundary of sphere. If the cuboid is inside the sphere, it calculates the integral and, if uncommented, insert the midpoint of the cuboid in the m_Midpoints vector. */ template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::CalculateEdgesInSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double cuboidRadius, int level ) { double xMin, xMax, yMin, yMax, zMin, zMax; double cuboidRadiusRecursion = cuboidRadius; PointType newMidpoint; int intersect = IntersectTheSphere( globalCoordinateMidpointCuboid, globalCoordinateMidpointSphere, cuboidRadiusRecursion); if( intersect == 1 ) { if (level < 4) { // cuboid intersect the sphere -> call CalculateEdgesInSphere (this method) for the subdivided cuboid cuboidRadiusRecursion = cuboidRadiusRecursion / 2.0; for(int i = -1; i < 2; i+=2) { for(int k = -1; k < 2; k+=2) { for(int j = -1; j < 2; j+=2) { newMidpoint[0] = globalCoordinateMidpointCuboid[0] + static_cast(i) * cuboidRadiusRecursion; newMidpoint[1] = globalCoordinateMidpointCuboid[1] + static_cast(k) * cuboidRadiusRecursion; newMidpoint[2] = globalCoordinateMidpointCuboid[2] + static_cast(j) * cuboidRadiusRecursion; this->CalculateEdgesInSphere( newMidpoint, globalCoordinateMidpointSphere, cuboidRadiusRecursion, level + 1 ); } } } } // last step of recursion -> on the boundary else { // Calculate the integral and take the half of it (because we are on the boundary) xMin = globalCoordinateMidpointCuboid[0] - cuboidRadius; xMax = globalCoordinateMidpointCuboid[0] + cuboidRadius; yMin = globalCoordinateMidpointCuboid[1] - cuboidRadius; yMax = globalCoordinateMidpointCuboid[1] + cuboidRadius; zMin = globalCoordinateMidpointCuboid[2] - cuboidRadius; zMax = globalCoordinateMidpointCuboid[2] + cuboidRadius; // size is in index coordinate -> multiply by the spacing -> global coordinate of the image boundary // yz Plane bool yzPlaneNotCrossYSection = xMin >= m_Origin[0] && xMax <= m_Size[0] * m_Spacing[0]; // xz Plane bool xzPlaneNotCrossYSection = yMin >= m_Origin[1] && yMax <= m_Size[1] * m_Spacing[1]; // xy Plane bool xyPlaneNotCrossZSection = zMin >= m_Origin[2] && zMax <= m_Size[2] * m_Spacing[2]; //check if the boundary of the integral is inside the image if( yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) { //double temp = this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ) * 0.5; m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ) * 0.5; m_Volume = m_Volume + pow( 2.0 * cuboidRadius, 3.0) / 2.0; } } } else if(intersect == 2) { // cuboid in the sphere // To insert the midpoint and the radius of the cuboid in a vector, so that we can visualise the midpoints, uncomment the next line and the line WriteXMLToTestTheCuboidInsideTheSphere(); // InsertPoints(globalCoordinateMidpointCuboid, cuboidRadius); // Calculate the integral boundary xMin = globalCoordinateMidpointCuboid[0] - cuboidRadius; xMax = globalCoordinateMidpointCuboid[0] + cuboidRadius; yMin = globalCoordinateMidpointCuboid[1] - cuboidRadius; yMax = globalCoordinateMidpointCuboid[1] + cuboidRadius; zMin = globalCoordinateMidpointCuboid[2] - cuboidRadius; zMax = globalCoordinateMidpointCuboid[2] + cuboidRadius; // size is in index coordinate -> multiply by the spacing -> global coordinate of the image boundary // yz Plane // bool yzPlaneAtOriginCrossXSection = xMin <= m_Origin[0] && xMax >= m_Origin[0]; // bool yzPlaneAtImageBorderCrossXSection = xMin <= m_Size[0] * m_Spacing[0] && xMax >= m_Size[0] * m_Spacing[0]; bool yzPlaneNotCrossYSection = xMin >= m_Origin[0] && xMax <= m_Size[0] * m_Spacing[0]; // xz Plane // bool xzPlaneAtOriginCrossYSection = yMin <= m_Origin[1] && yMax >= m_Origin[1]; // bool xzPlaneAtImageBorderCrossYSection = yMin <= m_Size[1] * m_Spacing[1] && yMax >= m_Size[1] * m_Spacing[1]; bool xzPlaneNotCrossYSection = yMin >= m_Origin[1] && yMax <= m_Size[1] * m_Spacing[1]; // xy Plane // bool xyPlaneAtOriginCrossZSection = zMin <= m_Origin[2] && zMax >= m_Origin[2]; // bool xyPlaneAtImageBorderCrossZSection = zMin <= m_Size[2] * m_Spacing[2] && zMax >= m_Size[2] * m_Spacing[2]; bool xyPlaneNotCrossZSection = zMin >= m_Origin[2] && zMax <= m_Size[2] * m_Spacing[2]; if( yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) { m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ); m_Volume = m_Volume + pow( 2.0 * cuboidRadius, 3.0); } // check if the boundary of the image intersect the cuboid and if yes, change the limits of the cuboid to be only inside the image; therefor we cut the sphere and neglect the part of it outside the image else /* if( // one plane crosses the cuboid ( (yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) ) || // two plane cross the cuboid (on the image edges possible) ( (yzPlaneAtOriginCrossXSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) || ( (yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) || (yzPlaneAtImageBorderCrossXSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneAtOriginCrossZSection) || (yzPlaneAtImageBorderCrossXSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtImageBorderCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtOriginCrossZSection) || (yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtImageBorderCrossZSection)) || (yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) ) ) */ { // x-Axis if(xMin <= m_Origin[0] && xMax >= m_Origin[0]) { xMin = m_Origin[0]; }else if(xMin <= m_Size[0] * m_Spacing[0] && xMax >= m_Size[0] * m_Spacing[0]) { xMax = m_Size[0] * m_Spacing[0]; } // y-Axis if(yMin <= m_Origin[1] && yMax >= m_Origin[1]) { yMin = m_Origin[1]; }else if(yMin <= m_Size[1] * m_Spacing[1] && yMax >= m_Size[1] * m_Spacing[1]) { yMax = m_Size[1] * m_Spacing[1]; } // z-Axis if(zMin <= m_Origin[2] && zMax >= m_Origin[2]) { zMin = m_Origin[2]; }else if(zMin <= m_Size[2] * m_Spacing[2] && zMax >= m_Size[2] * m_Spacing[2]) { zMax = m_Size[2] * m_Spacing[2]; } m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ); m_Volume = m_Volume + (xMax - xMin) * (yMax - yMin) * (zMax - zMin) ; } } } //----------------------------------------------------------------------------------------------------------------------- /* Start the octree recursion in eigth directions for the sphere with midpoint globalCoordinateMidpointSphere and, if uncommented, write this in a file, so that we can visualise it. */ template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::GenerateCuboidSegmentationInSphere(PointType globalCoordinateMidpointSphere) { double cuboidRadius = m_Radius * 0.5; PointType newMidpoint; for(int i = -1; i < 2; i+=2) { for(int k = -1; k < 2; k+=2) { for(int j = -1; j < 2; j+=2) { newMidpoint[0] = globalCoordinateMidpointSphere[0] + static_cast(i) * cuboidRadius; newMidpoint[1] = globalCoordinateMidpointSphere[1] + static_cast(k) * cuboidRadius; newMidpoint[2] = globalCoordinateMidpointSphere[2] + static_cast(j) * cuboidRadius; CalculateEdgesInSphere( newMidpoint, globalCoordinateMidpointSphere, cuboidRadius, 0); } } } if(m_WriteMPS) { m_WriteMPS = 0; // uncomment to write an .mps file to visualise the midpoints // std::cout << "Wrote .xml to visualise the midpoints." << std::endl; // WriteXMLToTestTheCuboidInsideTheSphere(); } } //---------------------------------------------------------------------------------------------------------------------- /* This class allows by the method CalculateTheMidpointAndTheMeanValueWithOctree() to find a sphere with a specified radius that has a maximal mean value over all sphere with that radius with midpoint inside or at the boundary of the image. We approximaze the sphere with the octree recursiv method. CalculateTheMidpointAndTheMeanValueWithOctree works as follows: 1. The for-loops traverse the region of interest and assume the current point to be the wanted sphere midpoint. 2. Calculate the mean value for that sphere. 3. Compare with the until-now-found-maximum and take the bigger one. */ template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::CalculateTheMidpointAndTheMeanValueWithOctree() { m_MeanValue = 0.0; double meanValueTemp; - PointType midpoint; - //typename MapContainerPoints::ElementIdentifier cuboidNumber = m_Midpoints.Size(); - // SetNormalDistributionValues(); - //double radius; - //double xMin, xMax, yMin, yMax, zMin, zMax; m_WriteMPS = 1; PointType globalCoordinateMidpointSphere; IndexType index; OutputImageRegionType regionOfInterest; IndexType indexR; indexR.SetElement( 0, m_RegionOfInterestMin[0] ); indexR.SetElement( 1, m_RegionOfInterestMin[1] ); indexR.SetElement( 2, m_RegionOfInterestMin[2] ); regionOfInterest.SetIndex(indexR); SizeType sizeROI; sizeROI.SetElement( 0, m_RegionOfInterestMax[0] - m_RegionOfInterestMin[0] + 1); sizeROI.SetElement( 1, m_RegionOfInterestMax[1] - m_RegionOfInterestMin[1] + 1); sizeROI.SetElement( 2, m_RegionOfInterestMax[2] - m_RegionOfInterestMin[2] + 1); regionOfInterest.SetSize(sizeROI); typename TOutputImage::Pointer image = this->GetOutput(0); IteratorType regionOfInterestIterator(image, regionOfInterest); for(regionOfInterestIterator.GoToBegin(); !regionOfInterestIterator.IsAtEnd(); ++regionOfInterestIterator) { index = regionOfInterestIterator.GetIndex(); image->TransformIndexToPhysicalPoint(index, globalCoordinateMidpointSphere); m_Volume = 0.0; m_meanValueTemp = 0.0; this->GenerateCuboidSegmentationInSphere(globalCoordinateMidpointSphere); meanValueTemp = m_meanValueTemp / m_Volume; // std::cout << "index: " << index <<" meanvalue: " << meanValueTemp << std::endl; if(meanValueTemp > m_MeanValue) { m_GlobalCoordinate = globalCoordinateMidpointSphere; m_MeanValue = meanValueTemp; m_SphereMidpoint = index; } } } //---------------------------------------------------------------------------------------------------------------------- /* Check if a cuboid intersect the sphere boundary. Returns 2, if the cuboid is inside the sphere; returns 1, if the cuboid intersects the sphere boundary and 0, if the cuboid is out of the sphere. */ template< class TOutputImage > unsigned int MultiGaussianImageSource< TOutputImage > ::IntersectTheSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double cuboidRadius) { unsigned int intersect = 1; PointType cuboidEdge; int count = 0; for(int i = -1; i < 2; i+=2) { for(int k = -1; k < 2; k+=2) { for(int j = -1; j < 2; j+=2) { cuboidEdge[0] = globalCoordinateMidpointCuboid[0] + static_cast(i) * cuboidRadius; cuboidEdge[1] = globalCoordinateMidpointCuboid[1] + static_cast(k) * cuboidRadius; cuboidEdge[2] = globalCoordinateMidpointCuboid[2] + static_cast(j) * cuboidRadius; if (globalCoordinateMidpointSphere.SquaredEuclideanDistanceTo(cuboidEdge) <= m_Radius * m_Radius) { ++count; } } } } if ( count == 0 ) { // cuboid not in the sphere intersect = 0; } if (count == 8 ) { // cuboid in the sphere intersect = 2; } return intersect; } //---------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > double MultiGaussianImageSource< TOutputImage > ::MultiGaussianFunctionValueAtPoint(double x, double y, double z) { //this claculate the mean value in the voxel //integrate over the voxel with midpoint [x, y, z] double summand0, summand1, summand2/*, power*/, value = 0.0, factor; double xMin, xMax, yMin, yMax, zMin, zMax, mean; mean = 0.0; // the for-loop represent the sum of the gaussian function xMin = x - m_Spacing[0] / 2.0; xMax = x + m_Spacing[0] / 2.0; yMin = y - m_Spacing[1] / 2.0; yMax = y + m_Spacing[1] / 2.0; zMin = z - m_Spacing[2] / 2.0; zMax = z + m_Spacing[2] / 2.0; for( unsigned int n = 0; n < m_NumberOfGaussians; ++n ) { summand0 = FunctionPhi( (xMax - m_CenterX[n]) / m_SigmaX[n] ) - FunctionPhi( (xMin - m_CenterX[n]) / m_SigmaX[n] ); summand1 = FunctionPhi( (yMax - m_CenterY[n]) / m_SigmaY[n] ) - FunctionPhi( (yMin - m_CenterY[n]) / m_SigmaY[n] ); summand2 = FunctionPhi( (zMax - m_CenterZ[n]) / m_SigmaZ[n] ) - FunctionPhi( (zMin - m_CenterZ[n]) / m_SigmaZ[n] ); value = summand0 * summand1 * summand2; factor = ( m_SigmaX[n] * m_SigmaY[n] * m_SigmaZ[n] ) * pow( 2.0 * itk::Math::pi, 1.5 ); mean = mean + factor * value * m_Altitude[n]; } value = mean / (m_Spacing[0] * m_Spacing[1] * m_Spacing[2] ); /* //this calculate the value of the gaussian at the midpoint of the voxel: double summand0, summand1, summand2, power, value = 0.0; // the for-loop represent the sum of the gaussian function for(unsigned int n =0; n < m_NumberOfGaussians; ++n) { summand0 = ( x - m_CenterX[n] ) / m_SigmaX[n]; summand1 = ( y - m_CenterY[n] ) / m_SigmaY[n]; summand2 = ( z - m_CenterZ[n] ) / m_SigmaZ[n]; power = summand0 * summand0 + summand1 * summand1 + summand2 * summand2; value = value + m_Altitude[n] * pow(itk::Math::e, -0.5 * power); } */ // std::cout << "X: " << xMin << " " << x << " "<< xMax << " Y: "<< yMin << " " << y << " " << yMax << " Z: "<< zMin << " "<< z << " " << zMax << " value: " << value << std::endl; return value; } //---------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::AddGaussian( VectorType x, VectorType y, VectorType z, VectorType sx, VectorType sy, VectorType sz, VectorType altitude) { for(unsigned int i = 0; i < x.size(); ++i) { m_CenterX.push_back( x[i] ); m_CenterY.push_back( y[i] ); m_CenterZ.push_back( z[i] ); m_SigmaX.push_back( sx[i] ); m_SigmaY.push_back( sy[i] ); m_SigmaZ.push_back( sz[i] ); m_Altitude.push_back(altitude[i]); } } //----------------------------------------------------------------------------------------------------------------------- template< typename TOutputImage > void MultiGaussianImageSource< TOutputImage > ::GenerateOutputInformation() { TOutputImage *output; IndexType index; index.Fill(0); output = this->GetOutput(0); typename TOutputImage::RegionType largestPossibleRegion; largestPossibleRegion.SetSize(this->m_Size); largestPossibleRegion.SetIndex(index); output->SetLargestPossibleRegion(largestPossibleRegion); output->SetSpacing(m_Spacing); output->SetOrigin(m_Origin); } //----------------------------------------------------------------------------------------------------------------------- template< typename TOutputImage > void MultiGaussianImageSource< TOutputImage > ::GenerateData() { itkDebugMacro(<< "Generating a image of scalars "); double valueReal; IndexType index; typename TOutputImage::Pointer image = this->GetOutput(0); image = this->GetOutput(0); image->SetBufferedRegion( image->GetRequestedRegion() ); image->Allocate(); IteratorType imageIt(image, image->GetLargestPossibleRegion()); PointType globalCoordinate; this->SetNormalDistributionValues(); for(imageIt.GoToBegin(); !imageIt.IsAtEnd(); ++imageIt) { valueReal = 0.0; index = imageIt.GetIndex(); image->TransformIndexToPhysicalPoint(imageIt.GetIndex(), globalCoordinate); valueReal = MultiGaussianFunctionValueAtPoint(globalCoordinate[0], globalCoordinate[1] ,globalCoordinate[2]); imageIt.Set(valueReal); } } //----------------------------------------------------------------------------------------------------------------------- /*This method is used to write a .mps file, so that we can visualize the midpoints of the approximated sphere as a scatterplot (for example with MITK Workbench). */ template< typename TOutputImage > void MultiGaussianImageSource< TOutputImage > ::WriteXMLToTestTheCuboidInsideTheSphere() { std::stringstream ss; int numberSummand = 1.0; //write an .mps test file itk::DOMNodeXMLWriter::Pointer xmlWriter; typedef itk::DOMNode::Pointer DOMNodeType; DOMNodeType domXML, domPointSetFile, domFileVersion, domPointSet, domPoint, domId, domX, domY, domZ; xmlWriter = itk::DOMNodeXMLWriter::New(); domXML = itk::DOMNode::New(); domXML->SetName("?xml"); domPointSetFile = itk::DOMNode::New(); domPointSetFile->SetName("point_set_file"); //domFileVersion = itk::DOMNode::New(); //domFileVersion->SetName("file_version"); domPointSet = itk::DOMNode::New(); domPointSet->SetName("point_set"); ss.str(""); ss << 1.0; domXML->SetAttribute("version", ss.str()); domXML->AddChildAtBegin(domPointSetFile); //domPointSetFile -> AddChildAtBegin(domFileVersion); domPointSetFile -> AddChildAtBegin(domPointSet); unsigned int cap = m_Midpoints.Size(); for(unsigned int iter = 0 ; iter < cap; ++iter) { domPoint = itk::DOMNode::New(); domPoint->SetName("point"); domX = itk::DOMNode::New(); domX->SetName("x"); domY = itk::DOMNode::New(); domY->SetName("y"); domZ = itk::DOMNode::New(); domZ->SetName("z"); domId = itk::DOMNode::New(); domId->SetName("id"); ss.str(""); ss << numberSummand; domId->AddTextChildAtBegin(ss.str()); domPoint -> AddChildAtEnd(domId); double scaleFactor = 10.0; PointType point = m_Midpoints.GetElement( numberSummand - 1 ); ss.str(""); ss << point[0] * scaleFactor; domX->AddTextChildAtBegin(ss.str()); domPoint -> AddChildAtEnd(domX); ss.str(""); ss << point[1] * scaleFactor; domY->AddTextChildAtBegin(ss.str()); domPoint -> AddChildAtEnd(domY); ss.str(""); ss << point[2] * scaleFactor; domZ->AddTextChildAtBegin(ss.str()); domPoint -> AddChildAtEnd(domZ); domPointSet -> AddChildAtEnd(domPoint); numberSummand += 1.0; } // .mps (Data) ss.str(""); ss << "C:/temp/CuboidsInTheSphere.mps"; std::string name = ss.str(); char * fileNamePointer = (char*) name.c_str(); xmlWriter->SetFileName( fileNamePointer); xmlWriter->SetInput( domXML ); xmlWriter->Update(); } //----------------------------------------------------------------------------------------------------------------------- template< typename TOutputImage > void MultiGaussianImageSource< TOutputImage > ::CalculateMaxAndMinInSphere() { IndexType index; typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType value; m_MaxValueInSphere = std::numeric_limits::min(); m_MinValueInSphere = std::numeric_limits::max(); int radInt, sizeRegion; OutputImageRegionType cuboidRegion; IndexType indexR; SizeType sizeR; int indexRegion, originAsIndex; for( unsigned int i = 0; i < 3; ++i ) { radInt = static_cast(m_Radius/m_Spacing[i]); indexRegion = m_SphereMidpoint[i] - radInt; originAsIndex = static_cast(m_Origin[i]/m_Spacing[i]); if( originAsIndex > indexRegion ) { indexR.SetElement(i, originAsIndex ); } else { indexR.SetElement(i, indexRegion ); } sizeRegion = 2 *radInt + 1; int sizeOutputImage = m_Size[i]; if( (indexR[i] + sizeRegion) > (originAsIndex + sizeOutputImage) ) { std::cout << "Not the entire sphere is in the image!" << std::endl; sizeR.SetElement(i, m_Size[i] - indexRegion ); } else { sizeR.SetElement(i, sizeRegion ); } } cuboidRegion.SetIndex(indexR); cuboidRegion.SetSize(sizeR); typename TOutputImage::Pointer image = this->GetOutput(0); IteratorType cuboidRegionOfInterestIterator(image, cuboidRegion); PointType globalCoordinate; for(cuboidRegionOfInterestIterator.GoToBegin(); !cuboidRegionOfInterestIterator.IsAtEnd(); ++cuboidRegionOfInterestIterator) { index = cuboidRegionOfInterestIterator.GetIndex(); if(IsInImage(index)) { image->TransformIndexToPhysicalPoint(cuboidRegionOfInterestIterator.GetIndex(), globalCoordinate); if( m_GlobalCoordinate.EuclideanDistanceTo(globalCoordinate) <= m_Radius ) { value = cuboidRegionOfInterestIterator.Get(); if(m_MaxValueInSphere < value) { m_MaxValueInSphere = value; m_MaxValueIndexInSphere = index; } if(m_MinValueInSphere > value) { m_MinValueInSphere = value; m_MinValueIndexInSphere = index; } } } } } //----------------------------------------------------------------------------------------------------------------------- template< typename TOutputImage > bool MultiGaussianImageSource< TOutputImage > ::IsInImage(IndexType index) { bool isInImage = true; int originAsIndex; for( unsigned int i = 0; i < 3; ++i ) { originAsIndex = static_cast(m_Origin[i]/m_Spacing[i]); int sizeOfOutputImage = m_Size[i]; if( index[i] < originAsIndex || index[i] > (originAsIndex + sizeOfOutputImage) ) return false; } return isInImage; } //----------------------------------------------------------------------------------------------------------------------- template< typename TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetNormalDistributionValues() { double temp[410] = { 0.5 , 0.50399 , 0.50798, 0.51197, 0.51595, 0.51994, 0.52392, 0.5279, 0.53188, 0.53586, 0.53983, 0.5438, 0.54776, 0.55172, 0.55567, 0.55962, 0.56356, 0.56749, 0.57142, 0.57535, 0.57926, 0.58317, 0.58706, 0.59095, 0.59483 , 0.59871, 0.60257, 0.60642, 0.61026, 0.61409, 0.61791, 0.62172, 0.62552, 0.6293, 0.63307, 0.63683, 0.64058, 0.64431, 0.64803, 0.65173, 0.65542, 0.6591, 0.66276, 0.6664, 0.67003, 0.67364, 0.67724, 0.68082, 0.68439, 0.68793, 0.69146, 0.69497, 0.69847, 0.70194, 0.7054, 0.70884, 0.71226, 0.71566, 0.71904, 0.7224, 0.72575, 0.72907, 0.73237, 0.73565, 0.73891, 0.74215, 0.74537, 0.74857, 0.75175, 0.7549, 0.75804, 0.76115, 0.76424, 0.7673, 0.77035, 0.77337, 0.77637, 0.77935, 0.7823, 0.78524, 0.78814, 0.79103, 0.79389, 0.79673, 0.79955, 0.80234, 0.80511, 0.80785, 0.81057, 0.81327, 0.81594, 0.81859, 0.82121, 0.82381, 0.82639, 0.82894, 0.83147, 0.83398, 0.83646, 0.83891, 0.84134, 0.84375, 0.84614, 0.84849, 0.85083, 0.85314, 0.85543, 0.85769, 0.85993, 0.86214, 0.86433, 0.8665, 0.86864, 0.87076, 0.87286, 0.87493, 0.87698, 0.879, 0.881, 0.88298, 0.88493, 0.88686, 0.88877, 0.89065, 0.89251, 0.89435, 0.89617, 0.89796, 0.89973, 0.90147, 0.9032, 0.9049, 0.90658, 0.90824, 0.90988, 0.91149, 0.91309, 0.91466, 0.91621, 0.91774, 0.91924, 0.92073, 0.9222, 0.92364, 0.92507, 0.92647, 0.92785, 0.92922, 0.93056, 0.93189, 0.93319, 0.93448, 0.93574, 0.93699, 0.93822, 0.93943, 0.94062, 0.94179, 0.94295, 0.94408, 0.9452, 0.9463, 0.94738, 0.94845, 0.9495, 0.95053, 0.95154, 0.95254, 0.95352, 0.95449, 0.95543, 0.95637, 0.95728, 0.95818, 0.95907, 0.95994, 0.9608, 0.96164, 0.96246, 0.96327, 0.96407, 0.96485, 0.96562, 0.96638, 0.96712, 0.96784, 0.96856, 0.96926, 0.96995, 0.97062, 0.97128, 0.97193, 0.97257, 0.9732, 0.97381, 0.97441, 0.975, 0.97558, 0.97615, 0.9767, 0.97725, 0.97778, 0.97831, 0.97882, 0.97932, 0.97982, 0.9803, 0.98077, 0.98124, 0.98169, 0.98214, 0.98257, 0.983, 0.98341, 0.98382, 0.98422, 0.98461, 0.985, 0.98537, 0.98574, 0.9861, 0.98645, 0.98679, 0.98713, 0.98745, 0.98778, 0.98809, 0.9884, 0.9887, 0.98899, 0.98928, 0.98956, 0.98983, 0.9901, 0.99036, 0.99061, 0.99086, 0.99111, 0.99134, 0.99158, 0.9918, 0.99202, 0.99224, 0.99245, 0.99266, 0.99286, 0.99305, 0.99324, 0.99343, 0.99361, 0.99379, 0.99396, 0.99413, 0.9943, 0.99446, 0.99461, 0.99477, 0.99492, 0.99506, 0.9952, 0.99534, 0.99547, 0.9956, 0.99573, 0.99585, 0.99598, 0.99609, 0.99621, 0.99632, 0.99643, 0.99653, 0.99664, 0.99674, 0.99683, 0.99693, 0.99702, 0.99711, 0.9972, 0.99728, 0.99736, 0.99744, 0.99752, 0.9976, 0.99767, 0.99774, 0.99781, 0.99788, 0.99795, 0.99801, 0.99807, 0.99813, 0.99819, 0.99825, 0.99831, 0.99836, 0.99841, 0.99846, 0.99851, 0.99856, 0.99861, 0.99865, 0.99869, 0.99874, 0.99878, 0.99882, 0.99886, 0.99889, 0.99893, 0.99896, 0.999, 0.99903, 0.99906, 0.9991, 0.99913, 0.99916, 0.99918, 0.99921, 0.99924, 0.99926, 0.99929, 0.99931, 0.99934, 0.99936, 0.99938, 0.9994, 0.99942, 0.99944, 0.99946, 0.99948, 0.9995, 0.99952, 0.99953, 0.99955, 0.99957, 0.99958, 0.9996, 0.99961, 0.99962, 0.99964, 0.99965, 0.99966, 0.99968, 0.99969, 0.9997, 0.99971, 0.99972, 0.99973, 0.99974, 0.99975, 0.99976, 0.99977, 0.99978, 0.99978, 0.99979, 0.9998, 0.99981, 0.99981, 0.99982, 0.99983, 0.99983, 0.99984, 0.99985, 0.99985, 0.99986, 0.99986, 0.99987, 0.99987, 0.99988, 0.99988, 0.99989, 0.99989, 0.9999, 0.9999, 0.9999, 0.99991, 0.99991, 0.99992, 0.99992, 0.99992, 0.99992, 0.99993, 0.99993, 0.99993, 0.99994, 0.99994, 0.99994, 0.99994, 0.99995, 0.99995, 0.99995, 0.99995, 0.99995, 0.99996, 0.99996, 0.99996, 0.99996, 0.99996, 0.99996, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99998, 0.99998, 0.99998, 0.99998 }; for(int i=0; i < 410; i++) { m_NormalDistValues[i] = temp[i]; } } } // end namespace itk #endif diff --git a/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.h b/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.h deleted file mode 100644 index 37352663b7..0000000000 --- a/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.h +++ /dev/null @@ -1,347 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ -#ifndef __mitkExtendedLabelStatisticsImageFilter -#define __mitkExtendedLabelStatisticsImageFilter - -#include "itkLabelStatisticsImageFilter.h" - -namespace itk -{ - /** - * \class ExtendedLabelStatisticsImageFilter - * \brief Extension of the itkLabelStatisticsImageFilter that also calculates the Skewness,Kurtosis,Entropy,Uniformity. - * - * This class inherits from the itkLabelStatisticsImageFilter and - * uses its results for the calculation of seven additional coefficients: - * the Skewness, Kurtosis, Uniformity, UPP, MPP, Entropy and Median - * - * - */ - template< class TInputImage, class TLabelImage > - class ExtendedLabelStatisticsImageFilter : public LabelStatisticsImageFilter< TInputImage, TLabelImage > - { - public: - - typedef ExtendedLabelStatisticsImageFilter Self; - typedef LabelStatisticsImageFilter < TInputImage, TLabelImage > Superclass; - typedef SmartPointer< Self > Pointer; - typedef SmartPointer< const Self > ConstPointer; - typedef typename Superclass::LabelPixelType LabelPixelType; - typedef typename Superclass::RealType RealType; - typedef typename Superclass::PixelType PixelType; - typedef typename Superclass::MapIterator MapIterator; - typedef typename Superclass::BoundingBoxType BoundingBoxType; - typedef typename Superclass::RegionType RegionType; - typedef itk::Statistics::Histogram HistogramType; - - itkFactorylessNewMacro( Self ); - itkCloneMacro( Self ); - itkTypeMacro(ExtendedLabelStatisticsImageFilter, LabelStatisticsImageFilter); - - - /** \class LabelStatistics - * \brief Statistics stored per label - * \ingroup ITKImageStatistics - */ - class LabelStatistics - { - public: - - // default constructor - LabelStatistics() - { - // initialized to the default values - m_Count = NumericTraits< IdentifierType >::ZeroValue(); - m_PositivePixelCount = NumericTraits< IdentifierType >::ZeroValue(); - m_Sum = NumericTraits< RealType >::ZeroValue(); - m_SumOfPositivePixels = NumericTraits< RealType >::ZeroValue(); - - m_SumOfSquares = NumericTraits< RealType >::ZeroValue(); - m_SumOfCubes = NumericTraits< RealType >::ZeroValue(); - m_SumOfQuadruples = NumericTraits< RealType >::ZeroValue(); - - // Set such that the first pixel encountered can be compared - m_Minimum = NumericTraits< RealType >::max(); - m_Maximum = NumericTraits< RealType >::NonpositiveMin(); - - // Default these to zero - m_Mean = NumericTraits< RealType >::ZeroValue(); - m_Sigma = NumericTraits< RealType >::ZeroValue(); - m_Variance = NumericTraits< RealType >::ZeroValue(); - m_MPP = NumericTraits< RealType >::ZeroValue(); - m_Median = NumericTraits< RealType >::ZeroValue(); - m_Uniformity = NumericTraits< RealType >::ZeroValue(); - m_UPP = NumericTraits< RealType >::ZeroValue(); - m_Entropy = NumericTraits< RealType >::ZeroValue(); - m_Skewness = NumericTraits< RealType >::ZeroValue(); - m_Kurtosis = NumericTraits< RealType >::ZeroValue(); - - unsigned int imageDimension = itkGetStaticConstMacro(ImageDimension); - m_BoundingBox.resize(imageDimension * 2); - for ( unsigned int i = 0; i < imageDimension * 2; i += 2 ) - { - m_BoundingBox[i] = NumericTraits< IndexValueType >::max(); - m_BoundingBox[i + 1] = NumericTraits< IndexValueType >::NonpositiveMin(); - } - m_Histogram = nullptr; - } - - // constructor with histogram enabled - LabelStatistics(int size, RealType lowerBound, RealType upperBound) - { - // initialized to the default values - m_Count = NumericTraits< IdentifierType >::ZeroValue(); - m_PositivePixelCount = NumericTraits< IdentifierType >::ZeroValue(); - m_Sum = NumericTraits< RealType >::ZeroValue(); - m_SumOfPositivePixels = NumericTraits< RealType >::ZeroValue(); - - m_SumOfSquares = NumericTraits< RealType >::ZeroValue(); - m_SumOfCubes = NumericTraits< RealType >::ZeroValue(); - m_SumOfQuadruples = NumericTraits< RealType >::ZeroValue(); - - // Set such that the first pixel encountered can be compared - m_Minimum = NumericTraits< RealType >::max(); - m_Maximum = NumericTraits< RealType >::NonpositiveMin(); - - // Default these to zero - m_Mean = NumericTraits< RealType >::ZeroValue(); - m_Sigma = NumericTraits< RealType >::ZeroValue(); - m_Variance = NumericTraits< RealType >::ZeroValue(); - m_MPP = NumericTraits< RealType >::ZeroValue(); - m_Median = NumericTraits< RealType >::ZeroValue(); - m_Uniformity = NumericTraits< RealType >::ZeroValue(); - m_UPP = NumericTraits< RealType >::ZeroValue(); - m_Entropy = NumericTraits< RealType >::ZeroValue(); - m_Skewness = NumericTraits< RealType >::ZeroValue(); - m_Kurtosis = NumericTraits< RealType >::ZeroValue(); - - - unsigned int imageDimension = itkGetStaticConstMacro(ImageDimension); - m_BoundingBox.resize(imageDimension * 2); - for ( unsigned int i = 0; i < imageDimension * 2; i += 2 ) - { - m_BoundingBox[i] = NumericTraits< IndexValueType >::max(); - m_BoundingBox[i + 1] = NumericTraits< IndexValueType >::NonpositiveMin(); - } - - // Histogram - m_Histogram = HistogramType::New(); - typename HistogramType::SizeType hsize; - typename HistogramType::MeasurementVectorType lb; - typename HistogramType::MeasurementVectorType ub; - hsize.SetSize(1); - lb.SetSize(1); - ub.SetSize(1); - m_Histogram->SetMeasurementVectorSize(1); - hsize[0] = size; - lb[0] = lowerBound; - ub[0] = upperBound; - m_Histogram->Initialize(hsize, lb, ub); - } - - // need copy constructor because of smart pointer to histogram - LabelStatistics(const LabelStatistics & l) - { - m_Count = l.m_Count; - m_Minimum = l.m_Minimum; - m_Maximum = l.m_Maximum; - m_Mean = l.m_Mean; - m_Sum = l.m_Sum; - m_SumOfSquares = l.m_SumOfSquares; - m_Sigma = l.m_Sigma; - m_Variance = l.m_Variance; - m_MPP = l.m_MPP; - m_Median = l.m_Median; - m_Uniformity = l.m_Uniformity; - m_UPP = l.m_UPP; - m_Entropy = l.m_Entropy; - m_Skewness = l.m_Skewness; - m_Kurtosis = l.m_Kurtosis; - m_BoundingBox = l.m_BoundingBox; - m_Histogram = l.m_Histogram; - m_SumOfPositivePixels = l.m_SumOfPositivePixels; - m_PositivePixelCount = l.m_PositivePixelCount; - m_SumOfCubes = l.m_SumOfCubes; - m_SumOfQuadruples = l.m_SumOfQuadruples; - } - - // added for completeness - LabelStatistics &operator= (const LabelStatistics& l) - { - if(this != &l) - { - m_Count = l.m_Count; - m_Minimum = l.m_Minimum; - m_Maximum = l.m_Maximum; - m_Mean = l.m_Mean; - m_Sum = l.m_Sum; - m_SumOfSquares = l.m_SumOfSquares; - m_Sigma = l.m_Sigma; - m_Variance = l.m_Variance; - m_MPP = l.m_MPP; - m_Median = l.m_Median; - m_Uniformity = l.m_Uniformity; - m_UPP = l.m_UPP; - m_Entropy = l.m_Entropy; - m_Skewness = l.m_Skewness; - m_Kurtosis = l.m_Kurtosis; - m_BoundingBox = l.m_BoundingBox; - m_Histogram = l.m_Histogram; - m_SumOfPositivePixels = l.m_SumOfPositivePixels; - m_PositivePixelCount = l.m_PositivePixelCount; - m_SumOfCubes = l.m_SumOfCubes; - m_SumOfQuadruples = l.m_SumOfQuadruples; - } - return *this; - } - - IdentifierType m_Count; - RealType m_Minimum; - RealType m_Maximum; - RealType m_Mean; - RealType m_Sum; - RealType m_SumOfSquares; - RealType m_Sigma; - RealType m_Variance; - RealType m_MPP; - RealType m_Median; - RealType m_Uniformity; - RealType m_UPP; - RealType m_Entropy; - RealType m_Skewness; - RealType m_Kurtosis; - IdentifierType m_PositivePixelCount; - RealType m_SumOfPositivePixels; - RealType m_SumOfCubes; - RealType m_SumOfQuadruples; - typename Superclass::BoundingBoxType m_BoundingBox; - typename HistogramType::Pointer m_Histogram; - }; - - /** Type of the map used to store data per label */ - typedef itksys::hash_map< LabelPixelType, LabelStatistics > MapType; - typedef typename itksys::hash_map< LabelPixelType, LabelStatistics >::const_iterator StatisticsMapConstIterator; - typedef typename itksys::hash_map< LabelPixelType, LabelStatistics >::iterator StatisticsMapIterator; - typedef IdentifierType MapSizeType; - - /** Type of the container used to store valid label values */ - typedef std::vector ValidLabelValuesContainerType; - - /** Return the computed Minimum for a label. */ - RealType GetMinimum(LabelPixelType label) const; - - /** Return the computed Maximum for a label. */ - RealType GetMaximum(LabelPixelType label) const; - - /** Return the computed Mean for a label. */ - RealType GetMean(LabelPixelType label) const; - - /** Return the computed Standard Deviation for a label. */ - RealType GetSigma(LabelPixelType label) const; - - /** Return the computed Variance for a label. */ - RealType GetVariance(LabelPixelType label) const; - - /** Return the computed bounding box for a label. */ - BoundingBoxType GetBoundingBox(LabelPixelType label) const; - - /** Return the computed region. */ - RegionType GetRegion(LabelPixelType label) const; - - /** Return the compute Sum for a label. */ - RealType GetSum(LabelPixelType label) const; - - /** Return the number of pixels for a label. */ - MapSizeType GetCount(LabelPixelType label) const; - - /** Return the histogram for a label */ - HistogramType::Pointer GetHistogram(LabelPixelType label) const; - - /*getter method for the new statistics*/ - RealType GetSkewness(LabelPixelType label) const; - RealType GetKurtosis(LabelPixelType label) const; - RealType GetUniformity( LabelPixelType label) const; - RealType GetMedian( LabelPixelType label) const; - RealType GetEntropy( LabelPixelType label) const; - RealType GetMPP( LabelPixelType label) const; - RealType GetUPP( LabelPixelType label) const; - - bool GetMaskingNonEmpty() const; - - std::list GetRelevantLabels() const; - - - /** specify global Histogram parameters. If the histogram parameters are set with this function, the same min and max value are used for all histograms. */ - void SetHistogramParameters(const int numBins, RealType lowerBound, - RealType upperBound); - - /** specify Histogram parameters for each label individually. Labels in the label image that are not represented in the std::maps here will receive global parameters (if available) */ - void SetHistogramParametersForLabels(std::map numBins, std::map lowerBound, - std::map upperBound); - - protected: - ExtendedLabelStatisticsImageFilter(): - m_GlobalHistogramParametersSet(false), - m_MaskNonEmpty(false), - m_LabelHistogramParametersSet(false), - m_PreferGlobalHistogramParameters(false) - { - m_NumBins.set_size(1); - } - - ~ExtendedLabelStatisticsImageFilter() override{} - - void AfterThreadedGenerateData() override; - - /** Initialize some accumulators before the threads run. */ - void BeforeThreadedGenerateData() override; - - /** Multi-thread version GenerateData. */ - void ThreadedGenerateData(const typename TInputImage::RegionType & - outputRegionForThread, - ThreadIdType threadId) override; - - /** Does the specified label exist? Can only be called after a call - * a call to Update(). */ - bool HasLabel(LabelPixelType label) const - { - return m_LabelStatistics.find(label) != m_LabelStatistics.end(); - } - - private: - std::vector< MapType > m_LabelStatisticsPerThread; - MapType m_LabelStatistics; - ValidLabelValuesContainerType m_ValidLabelValues; - - bool m_GlobalHistogramParametersSet; - - typename HistogramType::SizeType m_NumBins; - - RealType m_LowerBound; - RealType m_UpperBound; - - bool m_MaskNonEmpty; - - bool m_LabelHistogramParametersSet; - std::map m_LabelMin, m_LabelMax; - std::map m_LabelNBins; - bool m_PreferGlobalHistogramParameters; - - }; // end of class - -} // end namespace itk - -#ifndef ITK_MANUAL_INSTANTIATION -#include "mitkExtendedLabelStatisticsImageFilter.hxx" -#endif - -#endif diff --git a/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.hxx deleted file mode 100644 index 05223f2105..0000000000 --- a/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.hxx +++ /dev/null @@ -1,737 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ -#ifndef _mitkExtendedLabelStatisticsImageFilter_hxx -#define _mitkExtendedLabelStatisticsImageFilter_hxx - -#include "mitkExtendedLabelStatisticsImageFilter.h" - -#include "itkImageRegionConstIteratorWithIndex.h" -#include "itkImageRegionConstIterator.h" -#include -#include -#include "mitkNumericConstants.h" -#include "mitkLogMacros.h" -#include - -namespace itk -{ - template< class TInputImage, class TLabelImage > - bool - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetMaskingNonEmpty() const - { - return m_MaskNonEmpty; - } - - template< typename TInputImage, typename TLabelImage > - void - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::SetHistogramParameters(const int numBins, RealType lowerBound, RealType upperBound) - { - m_NumBins[0] = numBins; - m_LowerBound = lowerBound; - m_UpperBound = upperBound; - m_GlobalHistogramParametersSet = true; - m_PreferGlobalHistogramParameters = true; - this->Modified(); - } - - template< typename TInputImage, typename TLabelImage > - void - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::SetHistogramParametersForLabels(std::map numBins, std::map lowerBound, - std::map upperBound) - { - m_LabelMin = lowerBound; - m_LabelMax = upperBound; - m_LabelNBins = numBins; - m_LabelHistogramParametersSet = true; - m_PreferGlobalHistogramParameters = false; - this->Modified(); - } - - template< class TInputImage, class TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetUniformity(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Uniformity; - } - } - - - template< class TInputImage, class TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetMedian(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Median; - } - } - - template< class TInputImage, class TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetEntropy(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Entropy; - } - } - - template< class TInputImage, class TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetUPP(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_UPP; - } - } - - template< class TInputImage, class TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetMPP(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_MPP; - } - } - - template< class TInputImage, class TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetKurtosis(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Kurtosis; - } - } - - - template< class TInputImage, class TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetSkewness(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Skewness; - } - } - - - - template< typename TInputImage, typename TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetMinimum(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Minimum; - } - } - - template< typename TInputImage, typename TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetMaximum(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Maximum; - } - } - - template< typename TInputImage, typename TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetMean(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Mean; - } - } - - template< typename TInputImage, typename TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetSum(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Sum; - } - } - - template< typename TInputImage, typename TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetSigma(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Sigma; - } - } - - template< typename TInputImage, typename TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetVariance(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Variance; - } - } - - template< typename TInputImage, typename TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::BoundingBoxType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetBoundingBox(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_BoundingBox; - } - } - - template< typename TInputImage, typename TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RegionType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetRegion(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - typename Superclass::BoundingBoxType bbox = this->GetBoundingBox(label); - typename Superclass::IndexType index; - typename Superclass::SizeType size; - - unsigned int dimension = bbox.size() / 2; - - for ( unsigned int i = 0; i < dimension; i++ ) - { - index[i] = bbox[2 * i]; - size[i] = bbox[2 * i + 1] - bbox[2 * i] + 1; - } - typename Superclass::RegionType region; - region.SetSize(size); - region.SetIndex(index); - - return region; - } - } - - template< typename TInputImage, typename TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::MapSizeType - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetCount(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - return ( *mapIt ).second.m_Count; - } - } - - - template< typename TInputImage, typename TLabelImage > - typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::HistogramType::Pointer - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetHistogram(LabelPixelType label) const - { - StatisticsMapConstIterator mapIt; - - mapIt = m_LabelStatistics.find(label); - if ( mapIt == m_LabelStatistics.end() ) - { - mitkThrow() << "Label does not exist"; - } - else - { - // this will be zero if histograms have not been enabled - return ( *mapIt ).second.m_Histogram; - } - } - - - - template< typename TInputImage, typename TLabelImage > - void - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::BeforeThreadedGenerateData() - { - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); - - // Resize the thread temporaries - m_LabelStatisticsPerThread.resize(numberOfThreads); - - // Initialize the temporaries - for ( ThreadIdType i = 0; i < numberOfThreads; ++i ) - { - m_LabelStatisticsPerThread[i].clear(); - } - - // Initialize the final map - m_LabelStatistics.clear(); - } - - template< typename TInputImage, typename TLabelImage > - std::list - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::GetRelevantLabels() const - { - std::list< int> relevantLabels; - for (int i = 0; i < 4096; ++i ) - { - if ( this->HasLabel( i ) ) - { - relevantLabels.push_back( i ); - } - } - return relevantLabels; - } - - template< typename TInputImage, typename TLabelImage > - void - ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > - ::ThreadedGenerateData(const typename TInputImage::RegionType & outputRegionForThread, - ThreadIdType threadId) - { - - typename HistogramType::IndexType histogramIndex(1); - typename HistogramType::MeasurementVectorType histogramMeasurement(1); - - const SizeValueType size0 = outputRegionForThread.GetSize(0); - if( size0 == 0) - { - return; - } - - ImageLinearConstIteratorWithIndex< TInputImage > it (this->GetInput(), - outputRegionForThread); - - ImageScanlineConstIterator< TLabelImage > labelIt (this->GetLabelInput(), - outputRegionForThread); - - StatisticsMapIterator mapIt; - - // support progress methods/callbacks - const size_t numberOfLinesToProcess = outputRegionForThread.GetNumberOfPixels() / size0; - ProgressReporter progress( this, threadId, numberOfLinesToProcess ); - - typedef typename MapType::value_type MapValueType; - - // do the work - while ( !it.IsAtEnd() ) - { - while ( !it.IsAtEndOfLine() ) - { - const RealType & value = static_cast< RealType >( it.Get() ); - - const LabelPixelType & label = labelIt.Get(); - - // is the label already in this thread? - mapIt = m_LabelStatisticsPerThread[threadId].find(label); - if ( mapIt == m_LabelStatisticsPerThread[threadId].end() ) - { - // if global histogram parameters are set and preferred then use them - if ( m_PreferGlobalHistogramParameters && m_GlobalHistogramParametersSet ) - { - mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label, - LabelStatistics(m_NumBins[0], m_LowerBound, - m_UpperBound) ) ).first; - } - // if we have label histogram parameters then use them. If we encounter a label that has no parameters then use global settings if available - else if(!m_PreferGlobalHistogramParameters && m_LabelHistogramParametersSet) - { - typename std::map::iterator lbIt, ubIt; - typename std::map::iterator nbIt; - - lbIt = m_LabelMin.find(label); - ubIt = m_LabelMax.find(label); - nbIt = m_LabelNBins.find(label); - - // if any of the parameters is lacking for the current label but global histogram params are available, use the global parameters - if ((lbIt == m_LabelMin.end() || ubIt == m_LabelMax.end() || nbIt == m_LabelNBins.end()) && m_GlobalHistogramParametersSet) - { - mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label, - LabelStatistics(m_NumBins[0], m_LowerBound, - m_UpperBound) ) ).first; - } - // if any of the parameters is lacking for the current label and global histogram params are not available, dont use histograms for this label - else if ((lbIt == m_LabelMin.end() || ubIt == m_LabelMax.end() || nbIt == m_LabelNBins.end()) && !m_GlobalHistogramParametersSet) - { - mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label, - LabelStatistics() ) ).first; - } - // label histogram parameters are available, use them! - else - { - PixelType lowerBound, upperBound; - unsigned int nBins; - lowerBound = (*lbIt).second; - upperBound = (*ubIt).second; - nBins = (*nbIt).second; - mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label, - LabelStatistics(nBins, lowerBound, upperBound) ) ).first; - } - } - // neither global nor label specific histogram parameters are set -> don't use histograms - else - { - mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label, - LabelStatistics() ) ).first; - } - } - - typename MapType::mapped_type &labelStats = ( *mapIt ).second; - - // update the values for this label and this thread - if ( value < labelStats.m_Minimum ) - { - labelStats.m_Minimum = value; - } - if ( value > labelStats.m_Maximum ) - { - labelStats.m_Maximum = value; - } - - // bounding box is min,max pairs - for ( unsigned int i = 0; i < ( 2 * TInputImage::ImageDimension ); i += 2 ) - { - const typename TInputImage::IndexType & index = it.GetIndex(); - if ( labelStats.m_BoundingBox[i] > index[i / 2] ) - { - labelStats.m_BoundingBox[i] = index[i / 2]; - } - if ( labelStats.m_BoundingBox[i + 1] < index[i / 2] ) - { - labelStats.m_BoundingBox[i + 1] = index[i / 2]; - } - } - - labelStats.m_Sum += value; - labelStats.m_SumOfSquares += ( value * value ); - labelStats.m_Count++; - labelStats.m_SumOfCubes += std::pow(value, 3.); - labelStats.m_SumOfQuadruples += std::pow(value, 4.); - - if (value > 0) - { - labelStats.m_PositivePixelCount++; - labelStats.m_SumOfPositivePixels += value; - } - - // if enabled, update the histogram for this label - if ( labelStats.m_Histogram.IsNotNull() ) - { - histogramMeasurement[0] = value; - labelStats.m_Histogram->GetIndex(histogramMeasurement, histogramIndex); - labelStats.m_Histogram->IncreaseFrequencyOfIndex(histogramIndex, 1); - } - - ++labelIt; - ++it; - } - labelIt.NextLine(); - it.NextLine(); - progress.CompletedPixel(); - } - - } - - - template< class TInputImage, class TLabelImage > - void ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >:: - AfterThreadedGenerateData() - { - StatisticsMapIterator mapIt; - StatisticsMapConstIterator threadIt; - ThreadIdType i; - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); - - // Run through the map for each thread and accumulate the count, - // sum, and sumofsquares - for ( i = 0; i < numberOfThreads; i++ ) - { - // iterate over the map for this thread - for ( threadIt = m_LabelStatisticsPerThread[i].begin(); - threadIt != m_LabelStatisticsPerThread[i].end(); - ++threadIt ) - { - // does this label exist in the cumulative structure yet? - mapIt = m_LabelStatistics.find( ( *threadIt ).first ); - if ( mapIt == m_LabelStatistics.end() ) - { - // create a new entry - typedef typename MapType::value_type MapValueType; - if ( m_GlobalHistogramParametersSet || m_LabelHistogramParametersSet ) - { -// mapIt = m_LabelStatistics.insert( MapValueType( ( *threadIt ).first, -// LabelStatistics(m_NumBins[0], m_LowerBound, -// m_UpperBound) ) ).first; - mapIt = m_LabelStatistics.insert( MapValueType( *threadIt ) ).first; - continue; - } - else - { - mapIt = m_LabelStatistics.insert( MapValueType( ( *threadIt ).first, - LabelStatistics() ) ).first; - } - } - - - typename MapType::mapped_type &labelStats = ( *mapIt ).second; - - // accumulate the information from this thread - labelStats.m_Count += ( *threadIt ).second.m_Count; - labelStats.m_Sum += ( *threadIt ).second.m_Sum; - labelStats.m_SumOfSquares += ( *threadIt ).second.m_SumOfSquares; - labelStats.m_SumOfPositivePixels += ( *threadIt ).second.m_SumOfPositivePixels; - labelStats.m_PositivePixelCount += ( *threadIt ).second.m_PositivePixelCount; - labelStats.m_SumOfCubes += ( *threadIt ).second.m_SumOfCubes; - labelStats.m_SumOfQuadruples += ( *threadIt ).second.m_SumOfQuadruples; - - if ( labelStats.m_Minimum > ( *threadIt ).second.m_Minimum ) - { - labelStats.m_Minimum = ( *threadIt ).second.m_Minimum; - } - if ( labelStats.m_Maximum < ( *threadIt ).second.m_Maximum ) - { - labelStats.m_Maximum = ( *threadIt ).second.m_Maximum; - } - - //bounding box is min,max pairs - int dimension = labelStats.m_BoundingBox.size() / 2; - for ( int ii = 0; ii < ( dimension * 2 ); ii += 2 ) - { - if ( labelStats.m_BoundingBox[ii] > ( *threadIt ).second.m_BoundingBox[ii] ) - { - labelStats.m_BoundingBox[ii] = ( *threadIt ).second.m_BoundingBox[ii]; - } - if ( labelStats.m_BoundingBox[ii + 1] < ( *threadIt ).second.m_BoundingBox[ii + 1] ) - { - labelStats.m_BoundingBox[ii + 1] = ( *threadIt ).second.m_BoundingBox[ii + 1]; - } - } - - // if enabled, update the histogram for this label - if ( m_GlobalHistogramParametersSet || m_LabelHistogramParametersSet ) - { - typename HistogramType::IndexType index; - index.SetSize(1); - for ( unsigned int bin = 0; bin < labelStats.m_Histogram->Size(); bin++ ) - { - index[0] = bin; - labelStats.m_Histogram->IncreaseFrequency( bin, ( *threadIt ).second.m_Histogram->GetFrequency(bin) ); - } - } - } // end of thread map iterator loop - } // end of thread loop - - // compute the remainder of the statistics - for ( mapIt = m_LabelStatistics.begin(); - mapIt != m_LabelStatistics.end(); - ++mapIt ) - { - typename MapType::mapped_type &labelStats = ( *mapIt ).second; - - // mean - labelStats.m_Mean = labelStats.m_Sum - / static_cast< RealType >( labelStats.m_Count ); - - // MPP - labelStats.m_MPP = labelStats.m_SumOfPositivePixels - / static_cast< RealType >( labelStats.m_PositivePixelCount ); - - // variance - if ( labelStats.m_Count > 0 ) - { - // unbiased estimate of variance - LabelStatistics & ls = mapIt->second; - const RealType sumSquared = ls.m_Sum * ls.m_Sum; - const RealType count = static_cast< RealType >( ls.m_Count ); - - ls.m_Variance = ( ls.m_SumOfSquares - sumSquared / count ) / ( count ); - - RealType secondMoment = ls.m_SumOfSquares / count; - RealType thirdMoment = ls.m_SumOfCubes / count; - RealType fourthMoment = ls.m_SumOfQuadruples / count; - - ls.m_Skewness = (thirdMoment - 3. * secondMoment * ls.m_Mean + 2. * std::pow(ls.m_Mean, 3.)) / std::pow(secondMoment - std::pow(ls.m_Mean, 2.), 1.5); // see http://www.boost.org/doc/libs/1_51_0/doc/html/boost/accumulators/impl/skewness_impl.html - ls.m_Kurtosis = (fourthMoment - 4. * thirdMoment * ls.m_Mean + 6. * secondMoment * std::pow(ls.m_Mean, 2.) - 3. * std::pow(ls.m_Mean, 4.)) / std::pow(secondMoment - std::pow(ls.m_Mean, 2.), 2.); // see http://www.boost.org/doc/libs/1_51_0/doc/html/boost/accumulators/impl/kurtosis_impl.html, dropped -3 - } - else - { - labelStats.m_Variance = NumericTraits< RealType >::ZeroValue(); - labelStats.m_Skewness = NumericTraits< RealType >::ZeroValue(); - labelStats.m_Kurtosis = NumericTraits< RealType >::ZeroValue(); - } - - // sigma - labelStats.m_Sigma = std::sqrt( labelStats.m_Variance ); - - // histogram statistics - if (labelStats.m_Histogram.IsNotNull()) - { - mitk::HistogramStatisticsCalculator histStatCalc; - histStatCalc.SetHistogram(labelStats.m_Histogram); - histStatCalc.CalculateStatistics(); - labelStats.m_Median = histStatCalc.GetMedian(); - labelStats.m_Entropy = histStatCalc.GetEntropy(); - labelStats.m_Uniformity = histStatCalc.GetUniformity(); - labelStats.m_UPP = histStatCalc.GetUPP(); - } - - } - - { - //Now update the cached vector of valid labels. - m_ValidLabelValues.resize(0); - m_ValidLabelValues.reserve(m_LabelStatistics.size()); - for ( mapIt = m_LabelStatistics.begin(); - mapIt != m_LabelStatistics.end(); - ++mapIt ) - { - m_ValidLabelValues.push_back(mapIt->first); - } - } - } - -} // end namespace itk - -#endif diff --git a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.h b/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.h deleted file mode 100644 index 00236bce16..0000000000 --- a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.h +++ /dev/null @@ -1,207 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ -#ifndef __mitkExtendedStatisticsImageFilter -#define __mitkExtendedStatisticsImageFilter - -#include "itkStatisticsImageFilter.h" -#include -#include - -namespace itk -{ - /** - * \class ExtendedStatisticsImageFilter - * \brief Extension of the itkStatisticsImageFilter that also calculates the Skewness and Kurtosis. - * - * This class inherits from the itkStatisticsImageFilter and - * uses its results for the calculation of other additional coefficients: - * the Skewness and Kurtosis. - * - * As the StatisticsImageFilter stores the statistics in the outputs 1 to 6 by the - * StatisticsImageFilter, the skewness, kurtosis, MPP, UPP, Uniformity, Entropy and Median are stored in the outputs - * 7 to 14 by this filter. - */ - template< class TInputImage > - class ExtendedStatisticsImageFilter : public StatisticsImageFilter< TInputImage > - { - public: - /** Standard Self typedef */ - typedef ExtendedStatisticsImageFilter Self; - typedef StatisticsImageFilter< TInputImage > Superclass; - typedef SmartPointer< Self > Pointer; - typedef SmartPointer< const Self > ConstPointer; - typedef typename Superclass::RealType RealType; - typedef typename Superclass::RealObjectType RealObjectType; - typedef typename Superclass::PixelType PixelType; - - /** Histogram-related typedefs */ - typedef itk::Statistics::Histogram< RealType > HistogramType; - typedef typename HistogramType::Pointer HistogramPointer; - - itkFactorylessNewMacro( Self ); - itkCloneMacro( Self ); - itkTypeMacro( ExtendedStatisticsImageFilter, StatisticsImageFilter ); - - /** - * \brief Return the computed Skewness. - */ - double GetSkewness() const - { - return this->GetSkewnessOutput()->Get(); - } - - /** - * \brief Return the computed Median - */ - double GetMedian() const - { - return this->GetMedianOutput()->Get(); - } - - /** - * \brief Return the computed Kurtosis. - */ - double GetKurtosis() const - { - return this->GetKurtosisOutput()->Get(); - } - - /* \brief Return the computed MPP. - */ - double GetMPP() const - { - return this->GetMPPOutput()->Get(); - } - - /** - * \brief Return the computed Uniformity. - */ - double GetUniformity() const - { - return this->GetUniformityOutput()->Get(); - } - - /** - *\brief Return the computed Entropy. - */ - double GetEntropy() const - { - return this->GetEntropyOutput()->Get(); - } - - /** - * \brief Return the computed UPP. - */ - double GetUPP() const - { - return this->GetUPPOutput()->Get(); - } - - - /** - * \brief Return the computed Histogram. - */ - const typename HistogramType::Pointer - GetHistogram() - { - if (m_HistogramCalculated) - { - return m_Histogram; - } - else - { - return nullptr; - } - } - - - /** specify Histogram parameters */ - void SetHistogramParameters(const int numBins, RealType lowerBound, - RealType upperBound); - - protected: - - ExtendedStatisticsImageFilter(); - - ~ExtendedStatisticsImageFilter() override{}; - - void BeforeThreadedGenerateData() override; - - /** Multi-thread version GenerateData. */ - void ThreadedGenerateData(const typename StatisticsImageFilter::RegionType & - outputRegionForThread, - ThreadIdType threadId) override; - - /** - * brief Calls AfterThreadedGenerateData() of the superclass and the main methods - */ - void AfterThreadedGenerateData() override; - - - RealObjectType* GetSkewnessOutput(); - - const RealObjectType* GetSkewnessOutput() const; - - RealObjectType* GetKurtosisOutput(); - - const RealObjectType* GetKurtosisOutput() const; - - RealObjectType* GetMPPOutput(); - - const RealObjectType* GetMPPOutput() const; - - RealObjectType* GetEntropyOutput(); - - const RealObjectType* GetEntropyOutput() const; - - RealObjectType* GetUniformityOutput(); - - const RealObjectType* GetUniformityOutput() const; - - RealObjectType* GetUPPOutput(); - - const RealObjectType* GetUPPOutput() const; - - RealObjectType* GetMedianOutput(); - - const RealObjectType* GetMedianOutput() const; - - using Superclass::MakeOutput; - DataObject::Pointer MakeOutput( ProcessObject::DataObjectPointerArraySizeType idx ) override; - -private: - Array< RealType > m_ThreadSum; - Array< RealType > m_SumOfSquares; - Array< RealType > m_SumOfCubes; - Array< RealType > m_SumOfQuadruples; - Array< SizeValueType > m_Count; - Array< SizeValueType > m_PositivePixelCount; - Array< RealType > m_ThreadSumOfPositivePixels; - Array< PixelType > m_ThreadMin; - Array< PixelType > m_ThreadMax; - std::vector< HistogramPointer > m_HistogramPerThread; - HistogramPointer m_Histogram; - bool m_UseHistogram; - bool m_HistogramCalculated; - RealType m_LowerBound, m_UpperBound; - int m_NumBins; - - - }; // end of class - -} // end namespace itk - -#ifndef ITK_MANUAL_INSTANTIATION -#include "mitkExtendedStatisticsImageFilter.hxx" -#endif - -#endif diff --git a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx deleted file mode 100644 index 7ade1af4e9..0000000000 --- a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx +++ /dev/null @@ -1,470 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ -#ifndef __mitkExtendedStatisticsImageFilter_hxx -#define __mitkExtendedStatisticsImageFilter_hxx - -#include "mitkExtendedStatisticsImageFilter.h" -#include - - -namespace itk -{ - template< class TInputImage > - ExtendedStatisticsImageFilter< TInputImage >::ExtendedStatisticsImageFilter() - : StatisticsImageFilter< TInputImage >(), - m_ThreadSum(1), - m_SumOfSquares(1), - m_SumOfCubes(1), - m_SumOfQuadruples(1), - m_Count(1), - m_PositivePixelCount(1), - m_ThreadSumOfPositivePixels(1), - m_ThreadMin(1), - m_ThreadMax(1), - m_UseHistogram(false), - m_HistogramCalculated(false) - { - /* - * add the Skewness,Kurtosis,Entropy,Uniformity,MPP, UPP, Median to the other statistical calculated Values - * of the mitkStatisticsImageFilter as the 7th to the 13th Output - */ - for ( int i = 7; i < 14; ++i ) - { - typename RealObjectType::Pointer output = - static_cast< RealObjectType * >( this->MakeOutput(i).GetPointer() ); - this->ProcessObject::SetNthOutput( i, output.GetPointer() ); - } - - this->GetSkewnessOutput()->Set( 0.0 ); - this->GetKurtosisOutput()->Set( 0.0 ); - this->GetEntropyOutput()->Set( -1.0 ); - this->GetUniformityOutput()->Set( 0.0 ); - this->GetUPPOutput()->Set( 0.0 ); - this->GetMPPOutput()->Set( 0.0 ); - this->GetMedianOutput()->Set( 0.0 ); - - m_Histogram = ITK_NULLPTR; - m_HistogramPerThread.resize(0); - } - - template< class TInputImage > - DataObject::Pointer - ExtendedStatisticsImageFilter< TInputImage >::MakeOutput( ProcessObject::DataObjectPointerArraySizeType output) - { - switch ( output ) - { - case 7: - case 8: - case 9: - case 10: - case 11: - case 12: - { - return RealObjectType::New().GetPointer(); - } - case 13: - { - return RealObjectType::New().GetPointer(); - } - default: - { - // might as well make an image - return Superclass::MakeOutput( output ); - } - } - } - - template< class TInputImage > - typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetSkewnessOutput() - { - return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(7) ); - } - - template< class TInputImage > - const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetSkewnessOutput() const - { - return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(7) ); - } - - - template< class TInputImage > - typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetKurtosisOutput() - { - return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(8) ); - } - - template< class TInputImage > - const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetKurtosisOutput() const - { - return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(8) ); - } - - template< class TInputImage > - typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetUniformityOutput() - { - return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(9) ); - } - - template< class TInputImage > - const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetUniformityOutput() const - { - return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(9) ); - } - - - template< class TInputImage > - typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetEntropyOutput() - { - return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(10) ); - } - - template< class TInputImage > - const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetEntropyOutput() const - { - return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(10) ); - } - - template< class TInputImage > - typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetUPPOutput() - { - return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(11) ); - } - - template< class TInputImage > - const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetUPPOutput() const - { - return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(11) ); - } - - template< class TInputImage > - typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetMPPOutput() - { - return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(12) ); - } - - template< class TInputImage > - const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetMPPOutput() const - { - return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(12) ); - } - - template< class TInputImage > - typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetMedianOutput() - { - return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(13) ); - } - - template< class TInputImage > - const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * - ExtendedStatisticsImageFilter< TInputImage > - ::GetMedianOutput() const - { - return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(13) ); - } - - - template< typename TInputImage > - void - ExtendedStatisticsImageFilter< TInputImage > - ::BeforeThreadedGenerateData() - { - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); - - if (m_UseHistogram) - { - // Histogram - m_Histogram = HistogramType::New(); - typename HistogramType::SizeType hsize; - typename HistogramType::MeasurementVectorType lb; - typename HistogramType::MeasurementVectorType ub; - hsize.SetSize(1); - lb.SetSize(1); - ub.SetSize(1); - m_Histogram->SetMeasurementVectorSize(1); - hsize[0] = m_NumBins; - lb[0] = m_LowerBound; - ub[0] = m_UpperBound; - m_Histogram->Initialize(hsize, lb, ub); - - m_HistogramPerThread.resize(numberOfThreads); - - for (unsigned int i = 0; i < numberOfThreads; i++) - { - typename HistogramType::Pointer hist = HistogramType::New(); - typename HistogramType::SizeType hsize; - typename HistogramType::MeasurementVectorType lb; - typename HistogramType::MeasurementVectorType ub; - hsize.SetSize(1); - lb.SetSize(1); - ub.SetSize(1); - hist->SetMeasurementVectorSize(1); - hsize[0] = m_NumBins; - lb[0] = m_LowerBound; - ub[0] = m_UpperBound; - hist->Initialize(hsize, lb, ub); - m_HistogramPerThread[i] = hist; - } - } - - // Resize the thread temporaries - m_Count.SetSize(numberOfThreads); - m_SumOfSquares.SetSize(numberOfThreads); - m_SumOfCubes.SetSize(numberOfThreads); - m_SumOfQuadruples.SetSize(numberOfThreads); - m_ThreadSum.SetSize(numberOfThreads); - m_ThreadMin.SetSize(numberOfThreads); - m_ThreadMax.SetSize(numberOfThreads); - m_PositivePixelCount.SetSize(numberOfThreads); - m_ThreadSumOfPositivePixels.SetSize(numberOfThreads); - - // Initialize the temporaries - m_Count.Fill(NumericTraits< SizeValueType >::Zero); - m_ThreadSum.Fill(NumericTraits< RealType >::Zero); - m_SumOfSquares.Fill(NumericTraits< RealType >::Zero); - m_SumOfCubes.Fill(NumericTraits< RealType >::Zero); - m_SumOfQuadruples.Fill(NumericTraits< RealType >::Zero); - m_ThreadMin.Fill( NumericTraits< PixelType >::max() ); - m_ThreadMax.Fill( NumericTraits< PixelType >::NonpositiveMin() ); - m_PositivePixelCount.Fill(NumericTraits< SizeValueType >::Zero); - m_ThreadSumOfPositivePixels.Fill(NumericTraits< SizeValueType >::Zero); - } - - - template< typename TInputImage > - void - ExtendedStatisticsImageFilter< TInputImage > - ::ThreadedGenerateData(const typename StatisticsImageFilter::RegionType & - outputRegionForThread, - ThreadIdType threadId) - { - const SizeValueType size0 = outputRegionForThread.GetSize(0); - if( size0 == 0) - { - return; - } - RealType realValue; - PixelType value; - - typename HistogramType::IndexType histogramIndex(1); - typename HistogramType::MeasurementVectorType histogramMeasurement(1); - - RealType sum = NumericTraits< RealType >::ZeroValue(); - RealType sumOfPositivePixels = NumericTraits< RealType >::ZeroValue(); - RealType sumOfSquares = NumericTraits< RealType >::ZeroValue(); - RealType sumOfCubes = NumericTraits< RealType >::ZeroValue(); - RealType sumOfQuadruples = NumericTraits< RealType >::ZeroValue(); - SizeValueType count = NumericTraits< SizeValueType >::ZeroValue(); - SizeValueType countOfPositivePixels = NumericTraits< SizeValueType >::ZeroValue(); - PixelType min = NumericTraits< PixelType >::max(); - PixelType max = NumericTraits< PixelType >::NonpositiveMin(); - - ImageScanlineConstIterator< TInputImage > it (this->GetInput(), outputRegionForThread); - - // support progress methods/callbacks - const size_t numberOfLinesToProcess = outputRegionForThread.GetNumberOfPixels() / size0; - ProgressReporter progress( this, threadId, numberOfLinesToProcess ); - - // do the work - while ( !it.IsAtEnd() ) - { - while ( !it.IsAtEndOfLine() ) - { - value = it.Get(); - realValue = static_cast< RealType >( value ); - - if (m_UseHistogram) - { - histogramMeasurement[0] = value; - m_HistogramPerThread[threadId]->GetIndex(histogramMeasurement, histogramIndex); - m_HistogramPerThread[threadId]->IncreaseFrequencyOfIndex(histogramIndex, 1); - } - - if ( value < min ) - { - min = value; - } - if ( value > max ) - { - max = value; - } - if (value > 0) - { - sumOfPositivePixels += realValue; - ++countOfPositivePixels; - } - - sum += realValue; - sumOfSquares += ( realValue * realValue ); - sumOfCubes += std::pow(realValue, 3.); - sumOfQuadruples += std::pow(realValue, 4.); - ++count; - ++it; - } - it.NextLine(); - progress.CompletedPixel(); - } - - m_ThreadSum[threadId] = sum; - m_SumOfSquares[threadId] = sumOfSquares; - m_Count[threadId] = count; - m_ThreadMin[threadId] = min; - m_ThreadMax[threadId] = max; - m_ThreadSumOfPositivePixels[threadId] = sumOfPositivePixels; - m_PositivePixelCount[threadId] = countOfPositivePixels; - m_SumOfCubes[threadId] = sumOfCubes; - m_SumOfQuadruples[threadId] = sumOfQuadruples; - } - - - template< class TInputImage > - void - ExtendedStatisticsImageFilter< TInputImage > - ::AfterThreadedGenerateData() - { - ThreadIdType i; - SizeValueType count = 0; - SizeValueType countOfPositivePixels = 0; - RealType sumOfSquares = 0; - RealType sumOfCubes = 0; - RealType sumOfQuadruples = 0; - RealType sumOfPositivePixels = 0; - - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); - - PixelType minimum; - PixelType maximum; - RealType mean; - RealType meanOfPositivePixels; - RealType sigma; - RealType variance; - RealType skewness; - RealType kurtosis; - RealType sum; - - sum = sumOfSquares = sumOfCubes = sumOfQuadruples = NumericTraits< RealType >::ZeroValue(); - count = countOfPositivePixels = 0; - - // Find the min/max over all threads and accumulate count, sum and - // sum of squares - minimum = NumericTraits< PixelType >::max(); - maximum = NumericTraits< PixelType >::NonpositiveMin(); - for ( i = 0; i < numberOfThreads; i++ ) - { - count += m_Count[i]; - sum += m_ThreadSum[i]; - sumOfSquares += m_SumOfSquares[i]; - sumOfCubes += m_SumOfCubes[i]; - sumOfQuadruples += m_SumOfQuadruples[i]; - sumOfPositivePixels += m_ThreadSumOfPositivePixels[i]; - countOfPositivePixels += m_PositivePixelCount[i]; - - if ( m_ThreadMin[i] < minimum ) - { - minimum = m_ThreadMin[i]; - } - if ( m_ThreadMax[i] > maximum ) - { - maximum = m_ThreadMax[i]; - } - // if enabled, update the histogram for this label - if ( m_UseHistogram ) - { - typename HistogramType::IndexType index; - index.SetSize(1); - for ( int bin = 0; bin < m_NumBins; ++bin ) - { - index[0] = bin; - m_Histogram->IncreaseFrequency( bin, m_HistogramPerThread[i]->GetFrequency(bin) ); - } - } - } - // compute statistics - mean = sum / static_cast< RealType >( count ); - meanOfPositivePixels = sumOfPositivePixels / static_cast< RealType >( countOfPositivePixels ); - - // unbiased estimate - variance = ( sumOfSquares - ( sum * sum / static_cast< RealType >( count ) ) ) - / ( static_cast< RealType >( count ) ); - RealType secondMoment, thirdMoment, fourthMoment; - secondMoment = sumOfSquares / static_cast< RealType >( count ); - thirdMoment = sumOfCubes / static_cast< RealType >( count ); - fourthMoment = sumOfQuadruples / static_cast< RealType >( count ); - - skewness = (thirdMoment - 3. * secondMoment * mean + 2. * std::pow(mean, 3.)) / std::pow(secondMoment - std::pow(mean, 2.), 1.5); // see http://www.boost.org/doc/libs/1_51_0/doc/html/boost/accumulators/impl/skewness_impl.html - kurtosis = (fourthMoment - 4. * thirdMoment * mean + 6. * secondMoment * std::pow(mean, 2.) - 3. * std::pow(mean, 4.)) / std::pow(secondMoment - std::pow(mean, 2.), 2.); // see http://www.boost.org/doc/libs/1_46_1/doc/html/boost/accumulators/impl/kurtosis_impl.html, dropped -3 - sigma = std::sqrt(variance); - - // Set the outputs - this->GetMinimumOutput()->Set(minimum); - this->GetMaximumOutput()->Set(maximum); - this->GetMeanOutput()->Set(mean); - this->GetSigmaOutput()->Set(sigma); - this->GetVarianceOutput()->Set(variance); - this->GetSumOutput()->Set(sum); - this->GetKurtosisOutput()->Set(kurtosis); - this->GetSkewnessOutput()->Set(skewness); - this->GetMPPOutput()->Set(meanOfPositivePixels); - - if (m_UseHistogram) - { - mitk::HistogramStatisticsCalculator histStatCalc; - histStatCalc.SetHistogram(m_Histogram); - histStatCalc.CalculateStatistics(); - this->GetUniformityOutput()->Set(histStatCalc.GetUniformity()); - this->GetUPPOutput()->Set(histStatCalc.GetUPP()); - this->GetEntropyOutput()->Set(histStatCalc.GetEntropy()); - this->GetMedianOutput()->Set(histStatCalc.GetMedian()); - - } - m_HistogramCalculated = true; - } - - template< typename TInputImage > - void - ExtendedStatisticsImageFilter< TInputImage > - ::SetHistogramParameters(const int numBins, RealType lowerBound, RealType upperBound) - { - m_NumBins = numBins; - m_LowerBound = lowerBound; - m_UpperBound = upperBound; - m_UseHistogram = true; - } - -} - -#endif diff --git a/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp index 83814b422c..30c805a19c 100644 --- a/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp +++ b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp @@ -1,613 +1,612 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkImageAccessByItk.h" #include #include #include namespace mitk { HotspotMaskGenerator::HotspotMaskGenerator(): m_HotspotRadiusinMM(6.2035049089940), // radius of a 1cm3 sphere in mm m_HotspotMustBeCompletelyInsideImage(true), m_Label(1) { m_TimeStep = 0; m_InternalMask = mitk::Image::New(); m_InternalMaskUpdateTime = 0; } void HotspotMaskGenerator::SetInputImage(mitk::Image::Pointer inputImage) { if (inputImage != m_inputImage) { m_inputImage = inputImage; m_ConvolutionImageMaxIndex.set_size(inputImage->GetDimension()); m_ConvolutionImageMinIndex.set_size(inputImage->GetDimension()); this->Modified(); } } void HotspotMaskGenerator::SetMask(MaskGenerator::Pointer mask) { if (mask != m_Mask) { m_Mask = mask; this->Modified(); } } HotspotMaskGenerator::~HotspotMaskGenerator() { } void HotspotMaskGenerator::SetHotspotRadiusInMM(double radiusInMillimeter) { if(radiusInMillimeter != m_HotspotRadiusinMM) { m_HotspotRadiusinMM = radiusInMillimeter; this->Modified(); } } const double& HotspotMaskGenerator::GetHotspotRadiusinMM() const { return m_HotspotRadiusinMM; } bool HotspotMaskGenerator::GetHotspotMustBeCompletelyInsideImage() const { return m_HotspotMustBeCompletelyInsideImage; } void HotspotMaskGenerator::SetHotspotMustBeCompletelyInsideImage(bool mustBeCompletelyInImage) { if (m_HotspotMustBeCompletelyInsideImage != mustBeCompletelyInImage) { m_HotspotMustBeCompletelyInsideImage = mustBeCompletelyInImage; this->Modified(); } } mitk::Image::Pointer HotspotMaskGenerator::GetMask() { if (IsUpdateRequired()) { if ( m_inputImage.IsNull() ) { throw std::runtime_error( "Error: image empty!" ); } if ( m_TimeStep >= m_inputImage->GetTimeSteps() ) { throw std::runtime_error( "Error: invalid time step!" ); } mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput( m_inputImage ); imageTimeSelector->SetTimeNr( m_TimeStep ); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer timeSliceImage = imageTimeSelector->GetOutput(); m_internalImage = timeSliceImage; m_internalMask2D = nullptr; // is this correct when this variable holds a smart pointer? m_internalMask3D = nullptr; if ( m_Mask != nullptr ) { m_Mask->SetTimeStep(m_TimeStep); mitk::Image::Pointer timeSliceMask = m_Mask->GetMask(); if ( m_internalImage->GetDimension() == 3 ) { CastToItkImage(timeSliceMask, m_internalMask3D); AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 3, m_internalMask3D, m_Label); } else if ( m_internalImage->GetDimension() == 2 ) { CastToItkImage(timeSliceMask, m_internalMask2D); AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 2, m_internalMask2D, m_Label); } else { throw std::runtime_error( "Error: invalid image dimension" ); } } else { if ( m_internalImage->GetDimension() == 3 ) { AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 3, m_internalMask3D, m_Label); } else if ( m_internalImage->GetDimension() == 2 ) { AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 2, m_internalMask2D, m_Label); } else { throw std::runtime_error( "Error: invalid image dimension" ); } } this->Modified(); } m_InternalMaskUpdateTime = m_InternalMask->GetMTime(); return m_InternalMask; } void HotspotMaskGenerator::SetTimeStep(unsigned int timeStep) { if (m_TimeStep != timeStep) { m_TimeStep = timeStep; } } void HotspotMaskGenerator::SetLabel(unsigned short label) { if (label != m_Label) { m_Label = label; this->Modified(); } } vnl_vector HotspotMaskGenerator::GetConvolutionImageMinIndex() { this->GetMask(); // make sure we are up to date return m_ConvolutionImageMinIndex; } vnl_vector HotspotMaskGenerator::GetHotspotIndex() { this->GetMask(); // make sure we are up to date return m_ConvolutionImageMaxIndex; } template HotspotMaskGenerator::ImageExtrema HotspotMaskGenerator::CalculateExtremaWorld( const itk::Image* inputImage, typename itk::Image::Pointer maskImage, double neccessaryDistanceToImageBorderInMM, unsigned int label ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; typedef itk::ImageRegionConstIteratorWithIndex MaskImageIteratorType; typedef itk::ImageRegionConstIteratorWithIndex InputImageIndexIteratorType; typename ImageType::SpacingType spacing = inputImage->GetSpacing(); ImageExtrema minMax; minMax.Defined = false; minMax.MaxIndex.set_size(VImageDimension); minMax.MaxIndex.set_size(VImageDimension); typename ImageType::RegionType allowedExtremaRegion = inputImage->GetLargestPossibleRegion(); bool keepDistanceToImageBorders( neccessaryDistanceToImageBorderInMM > 0 ); if (keepDistanceToImageBorders) { itk::IndexValueType distanceInPixels[VImageDimension]; for(unsigned short dimension = 0; dimension < VImageDimension; ++dimension) { // To confirm that the whole hotspot is inside the image we have to keep a specific distance to the image-borders, which is as long as // the radius. To get the amount of indices we divide the radius by spacing and add 0.5 because voxels are center based: // For example with a radius of 2.2 and a spacing of 1 two indices are enough because 2.2 / 1 + 0.5 = 2.7 => 2. // But with a radius of 2.7 we need 3 indices because 2.7 / 1 + 0.5 = 3.2 => 3 distanceInPixels[dimension] = int( neccessaryDistanceToImageBorderInMM / spacing[dimension] + 0.5); } allowedExtremaRegion.ShrinkByRadius(distanceInPixels); } InputImageIndexIteratorType imageIndexIt(inputImage, allowedExtremaRegion); float maxValue = itk::NumericTraits::min(); float minValue = itk::NumericTraits::max(); typename ImageType::IndexType maxIndex; typename ImageType::IndexType minIndex; for(unsigned short i = 0; i < VImageDimension; ++i) { maxIndex[i] = 0; minIndex[i] = 0; } if (maskImage != nullptr) { MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion()); typename ImageType::IndexType imageIndex; - typename ImageType::PointType worldPosition; typename ImageType::IndexType maskIndex; for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt) { imageIndex = maskIndex = maskIt.GetIndex(); if(maskIt.Get() == label) { if( allowedExtremaRegion.IsInside(imageIndex) ) { imageIndexIt.SetIndex( imageIndex ); double value = imageIndexIt.Get(); minMax.Defined = true; //Calculate minimum, maximum and corresponding index-values if( value > maxValue ) { maxIndex = imageIndexIt.GetIndex(); maxValue = value; } if(value < minValue ) { minIndex = imageIndexIt.GetIndex(); minValue = value; } } } } } else { for(imageIndexIt.GoToBegin(); !imageIndexIt.IsAtEnd(); ++imageIndexIt) { double value = imageIndexIt.Get(); minMax.Defined = true; //Calculate minimum, maximum and corresponding index-values if( value > maxValue ) { maxIndex = imageIndexIt.GetIndex(); maxValue = value; } if(value < minValue ) { minIndex = imageIndexIt.GetIndex(); minValue = value; } } } minMax.MaxIndex.set_size(VImageDimension); minMax.MinIndex.set_size(VImageDimension); for(unsigned int i = 0; i < minMax.MaxIndex.size(); ++i) { minMax.MaxIndex[i] = maxIndex[i]; } for(unsigned int i = 0; i < minMax.MinIndex.size(); ++i) { minMax.MinIndex[i] = minIndex[i]; } minMax.Max = maxValue; minMax.Min = minValue; return minMax; } template itk::Size HotspotMaskGenerator::CalculateConvolutionKernelSize( double spacing[VImageDimension], double radiusInMM ) { typedef itk::Image< float, VImageDimension > KernelImageType; typedef typename KernelImageType::SizeType SizeType; SizeType maskSize; for(unsigned int i = 0; i < VImageDimension; ++i) { maskSize[i] = static_cast( 2 * radiusInMM / spacing[i]); // We always want an uneven size to have a clear center point in the convolution mask if(maskSize[i] % 2 == 0 ) { ++maskSize[i]; } } return maskSize; } template itk::SmartPointer< itk::Image > HotspotMaskGenerator::GenerateHotspotSearchConvolutionKernel(double mmPerPixel[VImageDimension], double radiusInMM ) { std::stringstream ss; for (unsigned int i = 0; i < VImageDimension; ++i) { ss << mmPerPixel[i]; if (i < VImageDimension -1) ss << ","; } MITK_DEBUG << "Update convolution kernel for spacing (" << ss.str() << ") and radius " << radiusInMM << "mm"; double radiusInMMSquared = radiusInMM * radiusInMM; typedef itk::Image< float, VImageDimension > KernelImageType; typename KernelImageType::Pointer convolutionKernel = KernelImageType::New(); // Calculate size and allocate mask image typedef typename KernelImageType::SizeType SizeType; SizeType maskSize = this->CalculateConvolutionKernelSize(mmPerPixel, radiusInMM); mitk::Point3D convolutionMaskCenterIndex; convolutionMaskCenterIndex.Fill(0.0); for(unsigned int i = 0; i < VImageDimension; ++i) { convolutionMaskCenterIndex[i] = 0.5 * (double)(maskSize[i]-1); } typedef typename KernelImageType::IndexType IndexType; IndexType maskIndex; maskIndex.Fill(0); typedef typename KernelImageType::RegionType RegionType; RegionType maskRegion; maskRegion.SetSize(maskSize); maskRegion.SetIndex(maskIndex); convolutionKernel->SetRegions(maskRegion); convolutionKernel->SetSpacing(mmPerPixel); convolutionKernel->Allocate(); // Fill mask image values by subsampling the image grid typedef itk::ImageRegionIteratorWithIndex MaskIteratorType; MaskIteratorType maskIt(convolutionKernel,maskRegion); int numberOfSubVoxelsPerDimension = 2; // per dimension! int numberOfSubVoxels = ::pow( static_cast(numberOfSubVoxelsPerDimension), static_cast(VImageDimension) ); double subVoxelSizeInPixels = 1.0 / (double)numberOfSubVoxelsPerDimension; double valueOfOneSubVoxel = 1.0 / (double)numberOfSubVoxels; mitk::Point3D subVoxelIndexPosition; double distanceSquared = 0.0; typedef itk::ContinuousIndex ContinuousIndexType; for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt) { ContinuousIndexType indexPoint(maskIt.GetIndex()); mitk::Point3D voxelPosition; for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension) { voxelPosition[dimension] = indexPoint[dimension]; } double maskValue = 0.0; mitk::Vector3D subVoxelOffset; subVoxelOffset.Fill(0.0); // iterate sub-voxels by iterating all possible offsets for (subVoxelOffset[0] = -0.5 + subVoxelSizeInPixels / 2.0; subVoxelOffset[0] < +0.5; subVoxelOffset[0] += subVoxelSizeInPixels) { for (subVoxelOffset[1] = -0.5 + subVoxelSizeInPixels / 2.0; subVoxelOffset[1] < +0.5; subVoxelOffset[1] += subVoxelSizeInPixels) { for (subVoxelOffset[2] = -0.5 + subVoxelSizeInPixels / 2.0; subVoxelOffset[2] < +0.5; subVoxelOffset[2] += subVoxelSizeInPixels) { subVoxelIndexPosition = voxelPosition + subVoxelOffset; // this COULD be integrated into the for-loops if neccessary (add voxelPosition to initializer and end condition) distanceSquared = (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0] * (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0] + (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1] * (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1] + (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2] * (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2]; if (distanceSquared <= radiusInMMSquared) { maskValue += valueOfOneSubVoxel; } } } } maskIt.Set( maskValue ); } return convolutionKernel; } template itk::SmartPointer > HotspotMaskGenerator::GenerateConvolutionImage( const itk::Image* inputImage ) { double mmPerPixel[VImageDimension]; for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension) { mmPerPixel[dimension] = inputImage->GetSpacing()[dimension]; } // update convolution kernel typedef itk::Image< float, VImageDimension > KernelImageType; typename KernelImageType::Pointer convolutionKernel = this->GenerateHotspotSearchConvolutionKernel(mmPerPixel, m_HotspotRadiusinMM); // update convolution image typedef itk::Image< TPixel, VImageDimension > InputImageType; typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType; typedef itk::FFTConvolutionImageFilter ConvolutionFilterType; typename ConvolutionFilterType::Pointer convolutionFilter = ConvolutionFilterType::New(); typedef itk::ConstantBoundaryCondition BoundaryConditionType; BoundaryConditionType boundaryCondition; boundaryCondition.SetConstant(0.0); if (m_HotspotMustBeCompletelyInsideImage) { // overwrite default boundary condition convolutionFilter->SetBoundaryCondition(&boundaryCondition); } convolutionFilter->SetInput(inputImage); convolutionFilter->SetKernelImage(convolutionKernel); convolutionFilter->SetNormalize(true); MITK_DEBUG << "Update Convolution image for hotspot search"; convolutionFilter->UpdateLargestPossibleRegion(); typename ConvolutionImageType::Pointer convolutionImage = convolutionFilter->GetOutput(); convolutionImage->SetSpacing( inputImage->GetSpacing() ); // only workaround because convolution filter seems to ignore spacing of input image return convolutionImage; } template < typename TPixel, unsigned int VImageDimension> void HotspotMaskGenerator::FillHotspotMaskPixels( itk::Image* maskImage, itk::Point sphereCenter, double sphereRadiusInMM ) { typedef itk::Image< TPixel, VImageDimension > MaskImageType; typedef itk::ImageRegionIteratorWithIndex MaskImageIteratorType; MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion()); typename MaskImageType::IndexType maskIndex; typename MaskImageType::PointType worldPosition; // this is not very smart. I would rather use a 0 initialized mask (not the case here -> blame CalculateHotspotMask) and find the region where I need to iterate over, then iterate only over the small region for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt) { maskIndex = maskIt.GetIndex(); maskImage->TransformIndexToPhysicalPoint(maskIndex, worldPosition); maskIt.Set( worldPosition.EuclideanDistanceTo(sphereCenter) <= sphereRadiusInMM ? 1 : 0 ); } } template void HotspotMaskGenerator::CalculateHotspotMask(itk::Image* inputImage, typename itk::Image::Pointer maskImage, unsigned int label) { typedef itk::Image< TPixel, VImageDimension > InputImageType; typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; typename ConvolutionImageType::Pointer convolutionImage = this->GenerateConvolutionImage(inputImage); if (convolutionImage.IsNull()) { MITK_ERROR << "Empty convolution image in CalculateHotspotStatistics(). We should never reach this state (logic error)."; throw std::logic_error("Empty convolution image in CalculateHotspotStatistics()"); } // if mask image is not defined, create an image of the same size as inputImage and fill it with 1's // there is maybe a better way to do this!? if (maskImage == nullptr) { maskImage = MaskImageType::New(); typename MaskImageType::RegionType maskRegion = inputImage->GetLargestPossibleRegion(); typename MaskImageType::SpacingType maskSpacing = inputImage->GetSpacing(); typename MaskImageType::PointType maskOrigin = inputImage->GetOrigin(); typename MaskImageType::DirectionType maskDirection = inputImage->GetDirection(); maskImage->SetRegions(maskRegion); maskImage->Allocate(); maskImage->SetOrigin(maskOrigin); maskImage->SetSpacing(maskSpacing); maskImage->SetDirection(maskDirection); maskImage->FillBuffer(1); label = 1; } // find maximum in convolution image, given the current mask double requiredDistanceToBorder = m_HotspotMustBeCompletelyInsideImage ? m_HotspotRadiusinMM : -1.0; ImageExtrema convolutionImageInformation = CalculateExtremaWorld(convolutionImage.GetPointer(), maskImage, requiredDistanceToBorder, label); bool isHotspotDefined = convolutionImageInformation.Defined; if (!isHotspotDefined) { MITK_ERROR << "No origin of hotspot-sphere was calculated!"; m_InternalMask = nullptr; } else { // create a binary mask around the "hotspot" region, fill the shape of a sphere around our hotspot center // typename DuplicatorType::Pointer copyMachine = DuplicatorType::New(); // copyMachine->SetInputImage(inputImage); // copyMachine->Update(); // typename CastFilterType::Pointer caster = CastFilterType::New(); // caster->SetInput( copyMachine->GetOutput() ); // caster->Update(); typename MaskImageType::Pointer hotspotMaskITK = MaskImageType::New(); hotspotMaskITK->SetOrigin(inputImage->GetOrigin()); hotspotMaskITK->SetSpacing(inputImage->GetSpacing()); hotspotMaskITK->SetLargestPossibleRegion(inputImage->GetLargestPossibleRegion()); hotspotMaskITK->SetBufferedRegion(inputImage->GetBufferedRegion()); hotspotMaskITK->SetDirection(inputImage->GetDirection()); hotspotMaskITK->SetNumberOfComponentsPerPixel(inputImage->GetNumberOfComponentsPerPixel()); hotspotMaskITK->Allocate(); hotspotMaskITK->FillBuffer(1); typedef typename InputImageType::IndexType IndexType; IndexType maskCenterIndex; for (unsigned int d =0; d< VImageDimension;++d) { maskCenterIndex[d]=convolutionImageInformation.MaxIndex[d]; } typename ConvolutionImageType::PointType maskCenter; inputImage->TransformIndexToPhysicalPoint(maskCenterIndex,maskCenter); FillHotspotMaskPixels(hotspotMaskITK.GetPointer(), maskCenter, m_HotspotRadiusinMM); //obtain mitk::Image::Pointer from itk::Image mitk::Image::Pointer hotspotMaskAsMITKImage = mitk::GrabItkImageMemory(hotspotMaskITK); m_InternalMask = hotspotMaskAsMITKImage; m_ConvolutionImageMaxIndex = convolutionImageInformation.MaxIndex; m_ConvolutionImageMinIndex = convolutionImageInformation.MinIndex; } } bool HotspotMaskGenerator::IsUpdateRequired() const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long internalMaskTimeStamp = m_InternalMask->GetMTime(); unsigned long maskGeneratorTimeStamp = m_Mask->GetMTime(); unsigned long inputImageTimeStamp = m_inputImage->GetMTime(); if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed { return true; } if (m_InternalMaskUpdateTime < maskGeneratorTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class { return true; } if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class { return true; } return false; } } diff --git a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp index bfa5041c2f..45720f16a3 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp +++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp @@ -1,552 +1,548 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkImageStatisticsCalculator.h" -#include -#include +#include +#include #include #include #include #include #include #include #include #include #include #include namespace mitk { void ImageStatisticsCalculator::SetInputImage(const mitk::Image *image) { if (image != m_Image) { m_Image = image; this->Modified(); } } void ImageStatisticsCalculator::SetMask(mitk::MaskGenerator *mask) { if (mask != m_MaskGenerator) { m_MaskGenerator = mask; this->Modified(); } } void ImageStatisticsCalculator::SetSecondaryMask(mitk::MaskGenerator *mask) { if (mask != m_SecondaryMaskGenerator) { m_SecondaryMaskGenerator = mask; this->Modified(); } } void ImageStatisticsCalculator::SetNBinsForHistogramStatistics(unsigned int nBins) { if (nBins != m_nBinsForHistogramStatistics) { m_nBinsForHistogramStatistics = nBins; this->Modified(); this->m_UseBinSizeOverNBins = false; } if (m_UseBinSizeOverNBins) { this->Modified(); this->m_UseBinSizeOverNBins = false; } } unsigned int ImageStatisticsCalculator::GetNBinsForHistogramStatistics() const { return m_nBinsForHistogramStatistics; } void ImageStatisticsCalculator::SetBinSizeForHistogramStatistics(double binSize) { if (binSize != m_binSizeForHistogramStatistics) { m_binSizeForHistogramStatistics = binSize; this->Modified(); this->m_UseBinSizeOverNBins = true; } if (!m_UseBinSizeOverNBins) { this->Modified(); this->m_UseBinSizeOverNBins = true; } } double ImageStatisticsCalculator::GetBinSizeForHistogramStatistics() const { return m_binSizeForHistogramStatistics; } mitk::ImageStatisticsContainer* ImageStatisticsCalculator::GetStatistics(LabelIndex label) { if (m_Image.IsNull()) { mitkThrow() << "no image"; } if (!m_Image->IsInitialized()) { mitkThrow() << "Image not initialized!"; } if (IsUpdateRequired(label)) { auto timeGeometry = m_Image->GetTimeGeometry(); // always compute statistics on all timesteps for (unsigned int timeStep = 0; timeStep < m_Image->GetTimeSteps(); timeStep++) { if (m_MaskGenerator.IsNotNull()) { m_MaskGenerator->SetTimeStep(timeStep); //See T25625: otherwise, the mask is not computed again after setting a different time step m_MaskGenerator->Modified(); m_InternalMask = m_MaskGenerator->GetMask(); if (m_MaskGenerator->GetReferenceImage().IsNotNull()) { m_InternalImageForStatistics = m_MaskGenerator->GetReferenceImage(); } else { m_InternalImageForStatistics = m_Image; } } else { m_InternalImageForStatistics = m_Image; } if (m_SecondaryMaskGenerator.IsNotNull()) { m_SecondaryMaskGenerator->SetTimeStep(timeStep); m_SecondaryMask = m_SecondaryMaskGenerator->GetMask(); } ImageTimeSelector::Pointer imgTimeSel = ImageTimeSelector::New(); imgTimeSel->SetInput(m_InternalImageForStatistics); imgTimeSel->SetTimeNr(timeStep); imgTimeSel->UpdateLargestPossibleRegion(); imgTimeSel->Update(); m_ImageTimeSlice = imgTimeSel->GetOutput(); // Calculate statistics with/without mask if (m_MaskGenerator.IsNull() && m_SecondaryMaskGenerator.IsNull()) { // 1) calculate statistics unmasked: AccessByItk_2(m_ImageTimeSlice, InternalCalculateStatisticsUnmasked, timeGeometry, timeStep) } else { // 2) calculate statistics masked AccessByItk_2(m_ImageTimeSlice, InternalCalculateStatisticsMasked, timeGeometry, timeStep) } } } auto it = m_StatisticContainers.find(label); if (it != m_StatisticContainers.end()) { return (it->second).GetPointer(); } else { mitkThrow() << "unknown label"; return nullptr; } } template void ImageStatisticsCalculator::InternalCalculateStatisticsUnmasked( typename itk::Image *image, const TimeGeometry *timeGeometry, TimeStepType timeStep) { typedef typename itk::Image ImageType; - typedef typename itk::ExtendedStatisticsImageFilter ImageStatisticsFilterType; + typedef typename mitk::StatisticsImageFilter ImageStatisticsFilterType; typedef typename itk::MinMaxImageFilterWithIndex MinMaxFilterType; // reset statistics container if exists ImageStatisticsContainer::Pointer statisticContainerForImage; LabelIndex labelNoMask = 1; auto it = m_StatisticContainers.find(labelNoMask); if (it != m_StatisticContainers.end()) { statisticContainerForImage = it->second; } else { statisticContainerForImage = ImageStatisticsContainer::New(); statisticContainerForImage->SetTimeGeometry(const_cast(timeGeometry)); m_StatisticContainers.emplace(labelNoMask, statisticContainerForImage); } auto statObj = ImageStatisticsContainer::ImageStatisticsObject(); typename ImageStatisticsFilterType::Pointer statisticsFilter = ImageStatisticsFilterType::New(); statisticsFilter->SetInput(image); statisticsFilter->SetCoordinateTolerance(0.001); statisticsFilter->SetDirectionTolerance(0.001); // TODO: this is single threaded. Implement our own image filter that does this multi threaded // typename itk::MinimumMaximumImageCalculator::Pointer imgMinMaxFilter = // itk::MinimumMaximumImageCalculator::New(); imgMinMaxFilter->SetImage(image); // imgMinMaxFilter->Compute(); vnl_vector minIndex, maxIndex; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetInput(image); minMaxFilter->UpdateLargestPossibleRegion(); typename ImageType::PixelType minval = minMaxFilter->GetMin(); typename ImageType::PixelType maxval = minMaxFilter->GetMax(); typename ImageType::IndexType tmpMinIndex = minMaxFilter->GetMinIndex(); typename ImageType::IndexType tmpMaxIndex = minMaxFilter->GetMaxIndex(); // typename ImageType::IndexType tmpMinIndex = imgMinMaxFilter->GetIndexOfMinimum(); // typename ImageType::IndexType tmpMaxIndex = imgMinMaxFilter->GetIndexOfMaximum(); minIndex.set_size(tmpMaxIndex.GetIndexDimension()); maxIndex.set_size(tmpMaxIndex.GetIndexDimension()); for (unsigned int i = 0; i < tmpMaxIndex.GetIndexDimension(); i++) { minIndex[i] = tmpMinIndex[i]; maxIndex[i] = tmpMaxIndex[i]; } statObj.AddStatistic(mitk::ImageStatisticsConstants::MINIMUMPOSITION(), minIndex); statObj.AddStatistic(mitk::ImageStatisticsConstants::MAXIMUMPOSITION(), maxIndex); // convert m_binSize in m_nBins if necessary unsigned int nBinsForHistogram; if (m_UseBinSizeOverNBins) { nBinsForHistogram = std::max(static_cast(std::ceil(maxval - minval)) / m_binSizeForHistogramStatistics, 10.); // do not allow less than 10 bins } else { nBinsForHistogram = m_nBinsForHistogramStatistics; } statisticsFilter->SetHistogramParameters(nBinsForHistogram, minval, maxval); try { statisticsFilter->Update(); } catch (const itk::ExceptionObject &e) { mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what(); } auto voxelVolume = GetVoxelVolume(image); auto numberOfPixels = image->GetLargestPossibleRegion().GetNumberOfPixels(); auto volume = static_cast(numberOfPixels) * voxelVolume; auto variance = statisticsFilter->GetSigma() * statisticsFilter->GetSigma(); auto rms = std::sqrt(std::pow(statisticsFilter->GetMean(), 2.) + statisticsFilter->GetVariance()); // variance = sigma^2 statObj.AddStatistic(mitk::ImageStatisticsConstants::NUMBEROFVOXELS(), static_cast(numberOfPixels)); statObj.AddStatistic(mitk::ImageStatisticsConstants::VOLUME(), volume); statObj.AddStatistic(mitk::ImageStatisticsConstants::MEAN(), statisticsFilter->GetMean()); statObj.AddStatistic(mitk::ImageStatisticsConstants::MINIMUM(), static_cast(statisticsFilter->GetMinimum())); statObj.AddStatistic(mitk::ImageStatisticsConstants::MAXIMUM(), static_cast(statisticsFilter->GetMaximum())); statObj.AddStatistic(mitk::ImageStatisticsConstants::STANDARDDEVIATION(), statisticsFilter->GetSigma()); statObj.AddStatistic(mitk::ImageStatisticsConstants::VARIANCE(), variance); statObj.AddStatistic(mitk::ImageStatisticsConstants::SKEWNESS(), statisticsFilter->GetSkewness()); statObj.AddStatistic(mitk::ImageStatisticsConstants::KURTOSIS(), statisticsFilter->GetKurtosis()); statObj.AddStatistic(mitk::ImageStatisticsConstants::RMS(), rms); statObj.AddStatistic(mitk::ImageStatisticsConstants::MPP(), statisticsFilter->GetMPP()); statObj.AddStatistic(mitk::ImageStatisticsConstants::ENTROPY(), statisticsFilter->GetEntropy()); statObj.AddStatistic(mitk::ImageStatisticsConstants::MEDIAN(), statisticsFilter->GetMedian()); statObj.AddStatistic(mitk::ImageStatisticsConstants::UNIFORMITY(), statisticsFilter->GetUniformity()); statObj.AddStatistic(mitk::ImageStatisticsConstants::UPP(), statisticsFilter->GetUPP()); - statObj.m_Histogram = statisticsFilter->GetHistogram().GetPointer(); + statObj.m_Histogram = statisticsFilter->GetHistogram(); statisticContainerForImage->SetStatisticsForTimeStep(timeStep, statObj); } template double ImageStatisticsCalculator::GetVoxelVolume(typename itk::Image *image) const { auto spacing = image->GetSpacing(); double voxelVolume = 1.; for (unsigned int i = 0; i < image->GetImageDimension(); i++) { voxelVolume *= spacing[i]; } return voxelVolume; } template void ImageStatisticsCalculator::InternalCalculateStatisticsMasked(typename itk::Image *image, const TimeGeometry *timeGeometry, unsigned int timeStep) { typedef itk::Image ImageType; typedef itk::Image MaskType; typedef typename MaskType::PixelType LabelPixelType; - typedef itk::ExtendedLabelStatisticsImageFilter ImageStatisticsFilterType; + typedef LabelStatisticsImageFilter ImageStatisticsFilterType; typedef MaskUtilities MaskUtilType; typedef typename itk::MinMaxLabelImageFilterWithIndex MinMaxLabelFilterType; - typedef typename ImageType::PixelType InputImgPixelType; // workaround: if m_SecondaryMaskGenerator ist not null but m_MaskGenerator is! (this is the case if we request a // 'ignore zuero valued pixels' mask in the gui but do not define a primary mask) bool swapMasks = false; if (m_SecondaryMask.IsNotNull() && m_InternalMask.IsNull()) { m_InternalMask = m_SecondaryMask; m_SecondaryMask = nullptr; swapMasks = true; } // maskImage has to have the same dimension as image typename MaskType::Pointer maskImage = MaskType::New(); try { // try to access the pixel values directly (no copying or casting). Only works if mask pixels are of pixelType // unsigned short maskImage = ImageToItkImage(m_InternalMask); } catch (const itk::ExceptionObject &) { // if the pixel type of the mask is not short, then we have to make a copy of m_InternalMask (and cast the values) CastToItkImage(m_InternalMask, maskImage); } // if we have a secondary mask (say a ignoreZeroPixelMask) we need to combine the masks (corresponds to AND) if (m_SecondaryMask.IsNotNull()) { // dirty workaround for a bug when pf mask + any other mask is used in conjunction. We need a proper fix for this // (Fabian Isensee is responsible and probably working on it!) if (m_InternalMask->GetDimension() == 2 && (m_SecondaryMask->GetDimension() == 3 || m_SecondaryMask->GetDimension() == 4)) { mitk::Image::ConstPointer old_img = m_SecondaryMaskGenerator->GetReferenceImage(); m_SecondaryMaskGenerator->SetInputImage(m_MaskGenerator->GetReferenceImage()); m_SecondaryMask = m_SecondaryMaskGenerator->GetMask(); m_SecondaryMaskGenerator->SetInputImage(old_img); } typename MaskType::Pointer secondaryMaskImage = MaskType::New(); secondaryMaskImage = ImageToItkImage(m_SecondaryMask); // secondary mask should be a ignore zero value pixel mask derived from image. it has to be cropped to the mask // region (which may be planar or simply smaller) typename MaskUtilities::Pointer secondaryMaskMaskUtil = MaskUtilities::New(); secondaryMaskMaskUtil->SetImage(secondaryMaskImage.GetPointer()); secondaryMaskMaskUtil->SetMask(maskImage.GetPointer()); typename MaskType::Pointer adaptedSecondaryMaskImage = secondaryMaskMaskUtil->ExtractMaskImageRegion(); typename itk::MaskImageFilter2::Pointer maskFilter = itk::MaskImageFilter2::New(); maskFilter->SetInput1(maskImage); maskFilter->SetInput2(adaptedSecondaryMaskImage); maskFilter->SetMaskingValue( 1); // all pixels of maskImage where secondaryMaskImage==1 will be kept, all the others are set to 0 maskFilter->UpdateLargestPossibleRegion(); maskImage = maskFilter->GetOutput(); } typename MaskUtilType::Pointer maskUtil = MaskUtilType::New(); maskUtil->SetImage(image); maskUtil->SetMask(maskImage.GetPointer()); // if mask is smaller than image, extract the image region where the mask is typename ImageType::Pointer adaptedImage = ImageType::New(); adaptedImage = maskUtil->ExtractMaskImageRegion(); // this also checks mask sanity // find min, max, minindex and maxindex typename MinMaxLabelFilterType::Pointer minMaxFilter = MinMaxLabelFilterType::New(); minMaxFilter->SetInput(adaptedImage); minMaxFilter->SetLabelInput(maskImage); minMaxFilter->UpdateLargestPossibleRegion(); // set histogram parameters for each label individually (min/max may be different for each label) - typedef typename std::map MapType; - typedef typename std::pair PairType; + typedef typename std::unordered_map MapType; std::vector relevantLabels = minMaxFilter->GetRelevantLabels(); MapType minVals; MapType maxVals; - std::map nBins; + std::unordered_map nBins; for (LabelPixelType label : relevantLabels) { - minVals.insert(PairType(label, minMaxFilter->GetMin(label))); - maxVals.insert(PairType(label, minMaxFilter->GetMax(label))); + minVals[label] = static_cast(minMaxFilter->GetMin(label)); + maxVals[label] = static_cast(minMaxFilter->GetMax(label)); unsigned int nBinsForHistogram; if (m_UseBinSizeOverNBins) { nBinsForHistogram = std::max(static_cast(std::ceil(minMaxFilter->GetMax(label) - minMaxFilter->GetMin(label))) / m_binSizeForHistogramStatistics, 10.); // do not allow less than 10 bins } else { nBinsForHistogram = m_nBinsForHistogramStatistics; } - nBins.insert(typename std::pair(label, nBinsForHistogram)); + nBins[label] = nBinsForHistogram; } typename ImageStatisticsFilterType::Pointer imageStatisticsFilter = ImageStatisticsFilterType::New(); imageStatisticsFilter->SetDirectionTolerance(0.001); imageStatisticsFilter->SetCoordinateTolerance(0.001); imageStatisticsFilter->SetInput(adaptedImage); imageStatisticsFilter->SetLabelInput(maskImage); - imageStatisticsFilter->SetHistogramParametersForLabels(nBins, minVals, maxVals); + imageStatisticsFilter->SetHistogramParameters(nBins, minVals, maxVals); imageStatisticsFilter->Update(); - std::list labels = imageStatisticsFilter->GetRelevantLabels(); + auto labels = imageStatisticsFilter->GetValidLabelValues(); auto it = labels.begin(); while (it != labels.end()) { ImageStatisticsContainer::Pointer statisticContainerForLabelImage; auto labelIt = m_StatisticContainers.find(*it); // reset if statisticContainer already exist if (labelIt != m_StatisticContainers.end()) { statisticContainerForLabelImage = labelIt->second; } // create new statisticContainer else { statisticContainerForLabelImage = ImageStatisticsContainer::New(); statisticContainerForLabelImage->SetTimeGeometry(const_cast(timeGeometry)); // link label (*it) to statisticContainer m_StatisticContainers.emplace(*it, statisticContainerForLabelImage); } ImageStatisticsContainer::ImageStatisticsObject statObj; // find min, max, minindex and maxindex // make sure to only look in the masked region, use a masker for this vnl_vector minIndex, maxIndex; mitk::Point3D worldCoordinateMin; mitk::Point3D worldCoordinateMax; mitk::Point3D indexCoordinateMin; mitk::Point3D indexCoordinateMax; m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMinIndex(*it), worldCoordinateMin); m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMaxIndex(*it), worldCoordinateMax); m_Image->GetGeometry()->WorldToIndex(worldCoordinateMin, indexCoordinateMin); m_Image->GetGeometry()->WorldToIndex(worldCoordinateMax, indexCoordinateMax); minIndex.set_size(3); maxIndex.set_size(3); // for (unsigned int i=0; i < tmpMaxIndex.GetIndexDimension(); i++) for (unsigned int i = 0; i < 3; i++) { minIndex[i] = indexCoordinateMin[i]; maxIndex[i] = indexCoordinateMax[i]; } statObj.AddStatistic(mitk::ImageStatisticsConstants::MINIMUMPOSITION(), minIndex); statObj.AddStatistic(mitk::ImageStatisticsConstants::MAXIMUMPOSITION(), maxIndex); - assert(std::abs(minMaxFilter->GetMax(*it) - imageStatisticsFilter->GetMaximum(*it)) < mitk::eps); - assert(std::abs(minMaxFilter->GetMin(*it) - imageStatisticsFilter->GetMinimum(*it)) < mitk::eps); - auto voxelVolume = GetVoxelVolume(image); auto numberOfVoxels = static_cast(imageStatisticsFilter->GetCount(*it)); auto volume = static_cast(numberOfVoxels) * voxelVolume; auto rms = std::sqrt(std::pow(imageStatisticsFilter->GetMean(*it), 2.) + imageStatisticsFilter->GetVariance(*it)); // variance = sigma^2 auto variance = imageStatisticsFilter->GetSigma(*it) * imageStatisticsFilter->GetSigma(*it); statObj.AddStatistic(mitk::ImageStatisticsConstants::NUMBEROFVOXELS(), numberOfVoxels); statObj.AddStatistic(mitk::ImageStatisticsConstants::VOLUME(), volume); statObj.AddStatistic(mitk::ImageStatisticsConstants::MEAN(), imageStatisticsFilter->GetMean(*it)); - statObj.AddStatistic(mitk::ImageStatisticsConstants::MINIMUM(), imageStatisticsFilter->GetMinimum(*it)); - statObj.AddStatistic(mitk::ImageStatisticsConstants::MAXIMUM(), imageStatisticsFilter->GetMaximum(*it)); + statObj.AddStatistic(mitk::ImageStatisticsConstants::MINIMUM(), + static_cast(imageStatisticsFilter->GetMinimum(*it))); + statObj.AddStatistic(mitk::ImageStatisticsConstants::MAXIMUM(), + static_cast(imageStatisticsFilter->GetMaximum(*it))); statObj.AddStatistic(mitk::ImageStatisticsConstants::STANDARDDEVIATION(), imageStatisticsFilter->GetSigma(*it)); statObj.AddStatistic(mitk::ImageStatisticsConstants::VARIANCE(), variance); statObj.AddStatistic(mitk::ImageStatisticsConstants::SKEWNESS(), imageStatisticsFilter->GetSkewness(*it)); statObj.AddStatistic(mitk::ImageStatisticsConstants::KURTOSIS(), imageStatisticsFilter->GetKurtosis(*it)); statObj.AddStatistic(mitk::ImageStatisticsConstants::RMS(), rms); statObj.AddStatistic(mitk::ImageStatisticsConstants::MPP(), imageStatisticsFilter->GetMPP(*it)); statObj.AddStatistic(mitk::ImageStatisticsConstants::ENTROPY(), imageStatisticsFilter->GetEntropy(*it)); statObj.AddStatistic(mitk::ImageStatisticsConstants::MEDIAN(), imageStatisticsFilter->GetMedian(*it)); statObj.AddStatistic(mitk::ImageStatisticsConstants::UNIFORMITY(), imageStatisticsFilter->GetUniformity(*it)); statObj.AddStatistic(mitk::ImageStatisticsConstants::UPP(), imageStatisticsFilter->GetUPP(*it)); - statObj.m_Histogram = imageStatisticsFilter->GetHistogram(*it).GetPointer(); - + statObj.m_Histogram = imageStatisticsFilter->GetHistogram(*it); statisticContainerForLabelImage->SetStatisticsForTimeStep(timeStep, statObj); ++it; } // swap maskGenerators back if (swapMasks) { m_SecondaryMask = m_InternalMask; m_InternalMask = nullptr; } } bool ImageStatisticsCalculator::IsUpdateRequired(LabelIndex label) const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long inputImageTimeStamp = m_Image->GetMTime(); auto it = m_StatisticContainers.find(label); if (it == m_StatisticContainers.end()) { return true; } unsigned long statisticsTimeStamp = it->second->GetMTime(); if (thisClassTimeStamp > statisticsTimeStamp) // inputs have changed { return true; } if (inputImageTimeStamp > statisticsTimeStamp) // image has changed { return true; } if (m_MaskGenerator.IsNotNull()) { unsigned long maskGeneratorTimeStamp = m_MaskGenerator->GetMTime(); if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a mask generator and it has changed { return true; } } if (m_SecondaryMaskGenerator.IsNotNull()) { unsigned long maskGeneratorTimeStamp = m_SecondaryMaskGenerator->GetMTime(); if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a secondary mask generator and it has changed { return true; } } return false; } } // namespace mitk diff --git a/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.h b/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.h new file mode 100644 index 0000000000..817418e2ce --- /dev/null +++ b/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.h @@ -0,0 +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. + +============================================================================*/ + +// This file is based on ITK's itkLabelStatisticsImageFilter.h + +#ifndef mitkLabelStatisticsImageFilter_h +#define mitkLabelStatisticsImageFilter_h + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace mitk +{ + template + class LabelStatisticsImageFilter : public itk::ImageSink + { + public: + using Self = LabelStatisticsImageFilter; + using Superclass = itk::ImageSink; + using Pointer = itk::SmartPointer; + using ConstPointer = itk::SmartPointer; + + itkFactorylessNewMacro(Self); + + itkTypeMacro(LabelStatisticsImageFilter, itk::ImageSink); + + using IndexType = typename TInputImage::IndexType; + using SizeType = typename TInputImage::SizeType; + using RegionType = typename TInputImage::RegionType; + using PixelType = typename TInputImage::PixelType; + using LabelPixelType = typename mitk::Label::PixelType; + + static constexpr unsigned int ImageDimension = TInputImage::ImageDimension; + + using RealType = typename itk::NumericTraits::RealType; + + using DataObjectPointer = typename itk::DataObject::Pointer; + + using RealObjectType = itk::SimpleDataObjectDecorator; + + using BoundingBoxType = std::vector; + + using HistogramType = itk::Statistics::Histogram; + using HistogramPointer = typename HistogramType::Pointer; + + class LabelStatistics + { + public: + LabelStatistics(); + LabelStatistics(unsigned int size, RealType lowerBound, RealType upperBound); + ~LabelStatistics(); + + itk::SizeValueType m_Count; + itk::SizeValueType m_CountOfPositivePixels; + RealType m_Min; + RealType m_Max; + RealType m_Mean; + itk::CompensatedSummation m_Sum; + itk::CompensatedSummation m_SumOfPositivePixels; + itk::CompensatedSummation m_SumOfSquares; + itk::CompensatedSummation m_SumOfCubes; + itk::CompensatedSummation m_SumOfQuadruples; + RealType m_Sigma; + RealType m_Variance; + RealType m_MPP; + RealType m_Median; + RealType m_Uniformity; + RealType m_UPP; + RealType m_Entropy; + RealType m_Skewness; + RealType m_Kurtosis; + BoundingBoxType m_BoundingBox; + HistogramPointer m_Histogram; + }; + + using MapType = std::unordered_map; + using MapIterator = typename MapType::iterator; + using MapConstIterator = typename MapType::const_iterator; + + using ValidLabelValuesContainerType = std::vector; + const ValidLabelValuesContainerType& GetValidLabelValues() const; + + void SetHistogramParameters( + const std::unordered_map& sizes, + const std::unordered_map& lowerBounds, + const std::unordered_map& upperBounds); + + using LabelImageType = itk::Image; + using ProcessObject = itk::ProcessObject; + + itkSetInputMacro(LabelInput, LabelImageType); + itkGetInputMacro(LabelInput, LabelImageType); + + bool HasLabel(LabelPixelType label) const; + unsigned int GetNumberOfObjects() const; + unsigned int GetNumberOfLabels() const; + + PixelType GetMinimum(LabelPixelType label) const; + PixelType GetMaximum(LabelPixelType label) const; + RealType GetMean(LabelPixelType label) const; + RealType GetSigma(LabelPixelType label) const; + RealType GetVariance(LabelPixelType label) const; + BoundingBoxType GetBoundingBox(LabelPixelType label) const; + RegionType GetRegion(LabelPixelType label) const; + RealType GetSum(LabelPixelType label) const; + RealType GetSumOfSquares(LabelPixelType label) const; + RealType GetSumOfCubes(LabelPixelType label) const; + RealType GetSumOfQuadruples(LabelPixelType label) const; + RealType GetSkewness(LabelPixelType label) const; + RealType GetKurtosis(LabelPixelType label) const; + RealType GetMPP(LabelPixelType label) const; + itk::SizeValueType GetCount(LabelPixelType label) const; + HistogramPointer GetHistogram(LabelPixelType label) const; + RealType GetEntropy(LabelPixelType label) const; + RealType GetUniformity(LabelPixelType label) const; + RealType GetUPP(LabelPixelType label) const; + RealType GetMedian(LabelPixelType label) const; + + protected: + LabelStatisticsImageFilter(); + ~LabelStatisticsImageFilter(); + + void BeforeStreamedGenerateData() override; + void ThreadedStreamedGenerateData(const RegionType&) override; + void AfterStreamedGenerateData() override; + + void PrintSelf(std::ostream& os, itk::Indent indent) const override; + + private: + const LabelStatistics& GetLabelStatistics(LabelPixelType label) const; + const LabelStatistics& GetLabelHistogramStatistics(LabelPixelType label) const; + + void MergeMap(MapType& map1, MapType& map2) const; + + MapType m_LabelStatistics; + ValidLabelValuesContainerType m_ValidLabelValues; + + bool m_ComputeHistograms; + std::unordered_map m_HistogramSizes; + std::unordered_map m_HistogramLowerBounds; + std::unordered_map m_HistogramUpperBounds; + + std::mutex m_Mutex; + }; +} + +#ifndef ITK_MANUAL_INSTANTIATION +#include +#endif + +#endif diff --git a/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.hxx new file mode 100644 index 0000000000..e5901c97c8 --- /dev/null +++ b/Modules/ImageStatistics/mitkLabelStatisticsImageFilter.hxx @@ -0,0 +1,544 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +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 mitkLabelStatisticsImageFilter_hxx +#define mitkLabelStatisticsImageFilter_hxx + +#include "mitkLabelStatisticsImageFilter.h" + +#include + +#include +#include + +template +mitk::LabelStatisticsImageFilter::LabelStatistics::LabelStatistics() + : m_Count(0), + m_CountOfPositivePixels(0), + m_Min(itk::NumericTraits::max()), + m_Max(itk::NumericTraits::NonpositiveMin()), + m_Mean(0), + m_Sum(0), + m_SumOfPositivePixels(0), + m_SumOfSquares(0), + m_SumOfCubes(0), + m_SumOfQuadruples(0), + m_Sigma(0), + m_Variance(0), + m_MPP(0), + m_Median(0), + m_Uniformity(0), + m_UPP(0), + m_Entropy(0), + m_Skewness(0), + m_Kurtosis(0) +{ + m_BoundingBox.resize(ImageDimension * 2); + + for (std::remove_const_t i = 0; i < ImageDimension * 2; i += 2) + { + m_BoundingBox[i] = itk::NumericTraits::max(); + m_BoundingBox[i + 1] = itk::NumericTraits::NonpositiveMin(); + } +} + +template +mitk::LabelStatisticsImageFilter::LabelStatistics::LabelStatistics(unsigned int size, RealType lowerBound, RealType upperBound) + : LabelStatistics() +{ + typename HistogramType::SizeType histogramSize; + histogramSize.SetSize(1); + histogramSize[0] = size; + + typename HistogramType::MeasurementVectorType histogramLowerBound; + histogramLowerBound.SetSize(1); + histogramLowerBound[0] = lowerBound; + + typename HistogramType::MeasurementVectorType histogramUpperBound; + histogramUpperBound.SetSize(1); + histogramUpperBound[0] = upperBound; + + m_Histogram = HistogramType::New(); + m_Histogram->SetMeasurementVectorSize(1); + m_Histogram->Initialize(histogramSize, histogramLowerBound, histogramUpperBound); +} + +template +mitk::LabelStatisticsImageFilter::LabelStatistics::~LabelStatistics() +{ +} + +template +mitk::LabelStatisticsImageFilter::LabelStatisticsImageFilter() + : m_ComputeHistograms(false) +{ + this->AddRequiredInputName("LabelInput"); +} + +template +mitk::LabelStatisticsImageFilter::~LabelStatisticsImageFilter() +{ +} + +template +auto mitk::LabelStatisticsImageFilter::BeforeStreamedGenerateData() -> void +{ + this->AllocateOutputs(); + m_LabelStatistics.clear(); +} + +template +auto mitk::LabelStatisticsImageFilter::ThreadedStreamedGenerateData(const RegionType& region) -> void +{ + if (0 == region.GetSize(0)) + return; + + MapType localStats; + + typename HistogramType::MeasurementVectorType histogramMeasurement(1); + typename HistogramType::IndexType histogramIndex(1); + + using TLabelImage = itk::Image; + + itk::ImageLinearConstIteratorWithIndex it(this->GetInput(), region); + itk::ImageScanlineConstIterator labelIt(this->GetLabelInput(), region); + + auto mapIt = localStats.end(); + + while (!it.IsAtEnd()) + { + while (!it.IsAtEndOfLine()) + { + const auto& value = static_cast(it.Get()); + const auto& index = it.GetIndex(); + const auto& label = labelIt.Get(); + + mapIt = localStats.find(label); + + if (mapIt == localStats.end()) + { + mapIt = m_ComputeHistograms + ? localStats.emplace(label, LabelStatistics(m_HistogramSizes[label], m_HistogramLowerBounds[label], m_HistogramUpperBounds[label])).first + : localStats.emplace(label, LabelStatistics()).first; + } + + auto& labelStats = mapIt->second; + + labelStats.m_Min = std::min(labelStats.m_Min, value); + labelStats.m_Max = std::max(labelStats.m_Max, value); + labelStats.m_Sum += value; + auto squareValue = value * value; + labelStats.m_SumOfSquares += squareValue; + labelStats.m_SumOfCubes += squareValue * value; + labelStats.m_SumOfQuadruples += squareValue * squareValue; + ++labelStats.m_Count; + + if (0 < value) + { + labelStats.m_SumOfPositivePixels += value; + ++labelStats.m_CountOfPositivePixels; + } + + for (unsigned int i = 0; i < (ImageDimension * 2); i += 2) + { + labelStats.m_BoundingBox[i] = std::min(labelStats.m_BoundingBox[i], index[i / 2]); + labelStats.m_BoundingBox[i + 1] = std::max(labelStats.m_BoundingBox[i + 1], index[i / 2]); + } + + if (m_ComputeHistograms) + { + histogramMeasurement[0] = value; + labelStats.m_Histogram->GetIndex(histogramMeasurement, histogramIndex); + labelStats.m_Histogram->IncreaseFrequencyOfIndex(histogramIndex, 1); + } + + ++labelIt; + ++it; + } + + labelIt.NextLine(); + it.NextLine(); + } + + // Merge localStats and m_LabelStatistics concurrently safe in a local copy + + while (true) + { + std::unique_lock lock(m_Mutex); + + if (m_LabelStatistics.empty()) + { + std::swap(m_LabelStatistics, localStats); + break; + } + else + { + MapType toMerge; + std::swap(m_LabelStatistics, toMerge); + lock.unlock(); + this->MergeMap(localStats, toMerge); + } + } +} + +template +auto mitk::LabelStatisticsImageFilter::AfterStreamedGenerateData() -> void +{ + Superclass::AfterStreamedGenerateData(); + + m_ValidLabelValues.clear(); + m_ValidLabelValues.reserve(m_LabelStatistics.size()); + + for (auto& val : m_LabelStatistics) + { + m_ValidLabelValues.push_back(val.first); + auto& stats = val.second; + + const auto& sum = stats.m_Sum.GetSum(); + const auto& sumOfSquares = stats.m_SumOfSquares.GetSum(); + const auto& sumOfCubes = stats.m_SumOfCubes.GetSum(); + const auto& sumOfQuadruples = stats.m_SumOfQuadruples.GetSum(); + const auto& sumOfPositivePixels = stats.m_SumOfPositivePixels.GetSum(); + + const RealType count = stats.m_Count; + const RealType countOfPositivePixels = stats.m_CountOfPositivePixels; + + stats.m_Mean = sum / count; + const auto& mean = stats.m_Mean; + + if (count > 1) + { + auto sumSquared = sum * sum; + stats.m_Variance = (sumOfSquares - sumSquared / count) / (count - 1.0); + } + else + { + stats.m_Variance = 0.0; + } + + stats.m_Sigma = std::sqrt(stats.m_Variance); + + const auto secondMoment = sumOfSquares / count; + const auto thirdMoment = sumOfCubes / count; + const auto fourthMoment = sumOfQuadruples / count; + + stats.m_Skewness = (thirdMoment - 3 * secondMoment * mean + 2 * std::pow(mean, 3)) / std::pow(secondMoment - std::pow(mean, 2), 1.5); + stats.m_Kurtosis = (fourthMoment - 4 * thirdMoment * mean + 6 * secondMoment * std::pow(mean, 2) - 3 * std::pow(mean, 4)) / std::pow(secondMoment - std::pow(mean, 2), 2); + stats.m_MPP = sumOfPositivePixels / countOfPositivePixels; + + if (m_ComputeHistograms) + { + mitk::HistogramStatisticsCalculator histogramStatisticsCalculator; + histogramStatisticsCalculator.SetHistogram(stats.m_Histogram); + histogramStatisticsCalculator.CalculateStatistics(); + + stats.m_Entropy = histogramStatisticsCalculator.GetEntropy(); + stats.m_Uniformity = histogramStatisticsCalculator.GetUniformity(); + stats.m_UPP = histogramStatisticsCalculator.GetUPP(); + stats.m_Median = histogramStatisticsCalculator.GetMedian(); + } + } +} + +template +auto mitk::LabelStatisticsImageFilter::SetHistogramParameters( + const std::unordered_map& sizes, + const std::unordered_map& lowerBounds, + const std::unordered_map& upperBounds) -> void +{ + bool modified = false; + + if (m_HistogramSizes != sizes) + { + m_HistogramSizes = sizes; + modified = true; + } + + if (m_HistogramLowerBounds != lowerBounds) + { + m_HistogramLowerBounds = lowerBounds; + modified = true; + } + + if (m_HistogramUpperBounds != upperBounds) + { + m_HistogramUpperBounds = upperBounds; + modified = true; + } + + m_ComputeHistograms = true; + + if (modified) + this->Modified(); +} + +template +auto mitk::LabelStatisticsImageFilter::MergeMap(MapType& map1, MapType& map2) const -> void +{ + for (auto& elem2 : map2) + { + auto iter1 = map1.find(elem2.first); + + if (map1.end() == iter1) + { + map1.emplace(elem2.first, std::move(elem2.second)); + } + else + { + const auto label = iter1->first; + auto& stats1 = iter1->second; + auto& stats2 = elem2.second; + + stats1.m_Min = std::min(stats1.m_Min, stats2.m_Min); + stats1.m_Max = std::max(stats1.m_Max, stats2.m_Max); + + stats1.m_Sum += stats2.m_Sum; + stats1.m_SumOfSquares += stats2.m_SumOfSquares; + stats1.m_SumOfCubes += stats2.m_SumOfCubes; + stats1.m_SumOfQuadruples += stats2.m_SumOfQuadruples; + stats1.m_Count += stats2.m_Count; + stats1.m_SumOfPositivePixels += stats2.m_SumOfPositivePixels; + stats1.m_CountOfPositivePixels += stats2.m_CountOfPositivePixels; + + for (unsigned int i = 0; i < (ImageDimension * 2); i += 2) + { + stats1.m_BoundingBox[i] = std::min(stats1.m_BoundingBox[i], stats2.m_BoundingBox[i]); + stats1.m_BoundingBox[i + 1] = std::max(stats1.m_BoundingBox[i + 1], stats2.m_BoundingBox[i + 1]); + } + + if (m_ComputeHistograms) + { + typename HistogramType::IndexType index; + index.SetSize(1); + + const auto histogramSize = m_HistogramSizes.at(label); + + for (unsigned int bin = 0; bin < histogramSize; ++bin) + { + index[0] = bin; + stats1.m_Histogram->IncreaseFrequency(bin, stats2.m_Histogram->GetFrequency(bin)); + } + } + } + } +} + +template +auto mitk::LabelStatisticsImageFilter::HasLabel(LabelPixelType label) const -> bool +{ + return m_LabelStatistics.find(label) != m_LabelStatistics.end(); +} + +template +auto mitk::LabelStatisticsImageFilter::GetNumberOfObjects() const -> unsigned int +{ + return static_cast(m_LabelStatistics.size()); +} + +template +auto mitk::LabelStatisticsImageFilter::GetNumberOfLabels() const -> unsigned int +{ + return this->GetNumberOfObjects(); +} + +template +auto mitk::LabelStatisticsImageFilter::GetValidLabelValues() const -> const ValidLabelValuesContainerType& +{ + return m_ValidLabelValues; +} + +template +auto mitk::LabelStatisticsImageFilter::GetLabelStatistics(LabelPixelType label) const -> const LabelStatistics& +{ + MapConstIterator it = m_LabelStatistics.find(label); + + if (m_LabelStatistics.end() != it) + return it->second; + + mitkThrow() << "Label " << label << " does not exist"; +} + +template +auto mitk::LabelStatisticsImageFilter::GetLabelHistogramStatistics(LabelPixelType label) const -> const LabelStatistics& +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + + if (m_ComputeHistograms && labelStatistics.m_Histogram.IsNotNull()) + return labelStatistics; + + mitkThrow() << "Histogram was not computed for label " << label; +} + +template +auto mitk::LabelStatisticsImageFilter::GetMinimum(LabelPixelType label) const -> PixelType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_Min; +} + +template +auto mitk::LabelStatisticsImageFilter::GetMaximum(LabelPixelType label) const -> PixelType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_Max; +} + +template +auto mitk::LabelStatisticsImageFilter::GetMean(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_Mean; +} + +template +auto mitk::LabelStatisticsImageFilter::GetSigma(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_Sigma; +} + +template +auto mitk::LabelStatisticsImageFilter::GetVariance(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_Variance; +} + +template +auto mitk::LabelStatisticsImageFilter::GetSkewness(LabelPixelType label) const ->RealType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_Skewness; +} + +template +auto mitk::LabelStatisticsImageFilter::GetKurtosis(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_Kurtosis; +} + +template +auto mitk::LabelStatisticsImageFilter::GetMPP(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_MPP; +} + +template +auto mitk::LabelStatisticsImageFilter::GetCount(LabelPixelType label) const-> itk::SizeValueType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_Count; +} + +template +auto mitk::LabelStatisticsImageFilter::GetHistogram(LabelPixelType label) const -> HistogramPointer +{ + const auto& labelStatistics = this->GetLabelHistogramStatistics(label); + return labelStatistics.m_Histogram; +} + +template +auto mitk::LabelStatisticsImageFilter::GetEntropy(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelHistogramStatistics(label); + return labelStatistics.m_Entropy; +} + +template +auto mitk::LabelStatisticsImageFilter::GetUniformity(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelHistogramStatistics(label); + return labelStatistics.m_Uniformity; +} + +template +auto mitk::LabelStatisticsImageFilter::GetUPP(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelHistogramStatistics(label); + return labelStatistics.m_UPP; +} + +template +auto mitk::LabelStatisticsImageFilter::GetMedian(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelHistogramStatistics(label); + return labelStatistics.m_Median; +} + +template +auto mitk::LabelStatisticsImageFilter::GetSum(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_Sum.GetSum(); +} + +template +auto mitk::LabelStatisticsImageFilter::GetSumOfSquares(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_SumOfSquares.GetSum(); +} + +template +auto mitk::LabelStatisticsImageFilter::GetSumOfCubes(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_SumOfCubes.GetSum(); +} + +template +auto mitk::LabelStatisticsImageFilter::GetSumOfQuadruples(LabelPixelType label) const -> RealType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_SumOfQuadruples.GetSum(); +} + +template +auto mitk::LabelStatisticsImageFilter::GetBoundingBox(LabelPixelType label) const -> BoundingBoxType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + return labelStatistics.m_BoundingBox; +} + +template +auto mitk::LabelStatisticsImageFilter::GetRegion(LabelPixelType label) const -> RegionType +{ + const auto& labelStatistics = this->GetLabelStatistics(label); + + IndexType index; + SizeType size; + + for (unsigned int i = 0; i < ImageDimension; ++i) + { + index[i] = labelStatistics.m_BoundingBox[2 * i]; + size[i] = labelStatistics.m_BoundingBox[2 * i + 1] - labelStatistics.m_BoundingBox[2 * i] + 1; + } + + RegionType region; + region.SetIndex(index); + region.SetSize(size); + + return region; +} + +template +void mitk::LabelStatisticsImageFilter::PrintSelf(std::ostream& os, itk::Indent indent) const +{ + Superclass::PrintSelf(os, indent); + + os << indent << "Number of labels: " << m_LabelStatistics.size() << std::endl; + os << indent << "Compute histograms: " << m_ComputeHistograms << std::endl; +} + +#endif diff --git a/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.h b/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.h index 99ec49358c..b2053c718b 100644 --- a/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.h +++ b/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.h @@ -1,96 +1,101 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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_MINMAXIMAGEFILTERWITHINDEX_H #define MITK_MINMAXIMAGEFILTERWITHINDEX_H #include #include #include #include namespace itk { template class MinMaxImageFilterWithIndex: public itk::ImageToImageFilter { public: /** Standard Self typedef */ typedef MinMaxImageFilterWithIndex Self; typedef ImageToImageFilter< TInputImage, TInputImage > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(MinMaxImageFilterWithIndex, ImageToImageFilter); typedef typename TInputImage::RegionType RegionType; typedef typename TInputImage::SizeType SizeType; typedef typename TInputImage::IndexType IndexType; typedef typename TInputImage::PixelType PixelType; typedef typename NumericTraits< PixelType >::RealType RealType; RealType GetMin() const { return m_Min; } RealType GetMax() const { return m_Max; } IndexType GetMinIndex() const { return m_MinIndex; } IndexType GetMaxIndex() const { return m_MaxIndex; } protected: + MinMaxImageFilterWithIndex() + { + this->DynamicMultiThreadingOff(); + } + void AllocateOutputs() override; void ThreadedGenerateData(const RegionType & outputRegionForThread, ThreadIdType threadId) override; void BeforeThreadedGenerateData() override; void AfterThreadedGenerateData() override; private: std::vector m_ThreadMin; std::vector m_ThreadMax; std::vector m_ThreadMinIndex; std::vector m_ThreadMaxIndex; PixelType m_Min; PixelType m_Max; IndexType m_MinIndex; IndexType m_MaxIndex; }; } #include "mitkMinMaxImageFilterWithIndex.hxx" #endif diff --git a/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx b/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx index 77b4510f4f..7d50c1224b 100644 --- a/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx +++ b/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx @@ -1,109 +1,109 @@ #ifndef MITK_MinMaxImageFilterWithIndex_HXX #define MITK_MinMaxImageFilterWithIndex_HXX #include #include namespace itk { template< typename TInputImage > void MinMaxImageFilterWithIndex< TInputImage >::AllocateOutputs() { // Pass the input through as the output typename TInputImage::Pointer image = const_cast< TInputImage * >( this->GetInput() ); this->GraftOutput(image); // Nothing that needs to be allocated for the remaining outputs } template< typename TInputImage > void MinMaxImageFilterWithIndex< TInputImage >::ThreadedGenerateData(const RegionType & outputRegionForThread, ThreadIdType threadId) { const SizeValueType size0 = outputRegionForThread.GetSize(0); if( size0 == 0) { return; } PixelType value; PixelType threadMin, threadMax; IndexType threadMinIndex; IndexType threadMaxIndex; threadMinIndex.Fill(0); threadMaxIndex.Fill(0); threadMin = std::numeric_limits::max(); threadMax = std::numeric_limits::min(); ImageRegionConstIteratorWithIndex< TInputImage > it (this->GetInput(), outputRegionForThread); // do the work while ( !it.IsAtEnd() ) { value = it.Get(); if (value < threadMin) { threadMin = value; threadMinIndex = it.GetIndex(); } if (value > threadMax) { threadMax = value; threadMaxIndex = it.GetIndex(); } ++it; } m_ThreadMax[threadId] = threadMax; m_ThreadMin[threadId] = threadMin; m_ThreadMaxIndex[threadId] = threadMaxIndex; m_ThreadMinIndex[threadId] = threadMinIndex; } template< typename TInputImage > void MinMaxImageFilterWithIndex< TInputImage >::BeforeThreadedGenerateData() { - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); + ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits(); m_ThreadMin.resize(numberOfThreads); m_ThreadMax.resize(numberOfThreads); m_ThreadMinIndex.resize(numberOfThreads); m_ThreadMaxIndex.resize(numberOfThreads); for (unsigned int i =0; i < numberOfThreads; i++) { m_ThreadMin[i] = std::numeric_limits::max(); m_ThreadMax[i] = std::numeric_limits::min(); } m_Min = std::numeric_limits::max(); m_Max = std::numeric_limits::min(); } template< typename TInputImage > void MinMaxImageFilterWithIndex< TInputImage >::AfterThreadedGenerateData() { - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); + ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits(); for (ThreadIdType i = 0; i < numberOfThreads; i++) { if (m_ThreadMin[i] < m_Min) { m_Min = m_ThreadMin[i]; m_MinIndex = m_ThreadMinIndex[i]; } if (m_ThreadMax[i] > m_Max) { m_Max = m_ThreadMax[i]; m_MaxIndex = m_ThreadMaxIndex[i]; } } } } #endif diff --git a/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.h b/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.h index 2d53595178..ff38ff3d71 100644 --- a/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.h +++ b/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.h @@ -1,189 +1,194 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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_MINMAXLABELIMAGEFILTERWITHINDEX_H #define MITK_MINMAXLABELIMAGEFILTERWITHINDEX_H #include #include #include #include -#include "itksys/hash_map.hxx" +#include namespace itk { template class MinMaxLabelImageFilterWithIndex: public itk::ImageToImageFilter { public: /** Standard Self typedef */ typedef MinMaxLabelImageFilterWithIndex Self; typedef ImageToImageFilter< TInputImage, TInputImage > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Runtime information support. */ itkTypeMacro(MinMaxLabelImageFilterWithIndex, ImageToImageFilter); typedef typename TInputImage::RegionType RegionType; typedef typename TInputImage::SizeType SizeType; typedef typename TInputImage::IndexType IndexType; typedef typename TInputImage::PixelType PixelType; typedef typename NumericTraits< PixelType >::RealType RealType; typedef typename TLabelImage::RegionType LabelRegionType; typedef typename TLabelImage::SizeType LabelSizeType; typedef typename TLabelImage::IndexType LabelIndexType; typedef typename TLabelImage::PixelType LabelPixelType; /** * @brief The LabelExtrema class is just a container for global min/max values and their indices as well as all min and max values (+indices) of the mask labels */ class LabelExtrema { public: PixelType m_Min, m_Max; IndexType m_MinIndex, m_MaxIndex; LabelExtrema(): m_Min(std::numeric_limits::max()), m_Max(std::numeric_limits::min()) {} }; - typedef typename itksys::hash_map ExtremaMapType; - typedef typename ExtremaMapType::iterator ExtremaMapTypeIterator; - typedef typename ExtremaMapType::const_iterator ExtremaMapTypeConstIterator; - typedef typename ExtremaMapType::value_type MapValueType; + typedef typename std::unordered_map ExtremaMapType; + typedef typename ExtremaMapType::iterator ExtremaMapTypeIterator; + typedef typename ExtremaMapType::const_iterator ExtremaMapTypeConstIterator; + typedef typename ExtremaMapType::value_type MapValueType; PixelType GetMin(LabelPixelType label) const { ExtremaMapTypeConstIterator it = m_LabelExtrema.find(label); if (it == m_LabelExtrema.end()) { MITK_ERROR << "invalid label"; } return (*it).second.m_Min; } PixelType GetMax(LabelPixelType label) const { ExtremaMapTypeConstIterator it = m_LabelExtrema.find(label); if (it == m_LabelExtrema.end()) { MITK_ERROR << "invalid label"; } return (*it).second.m_Max; } /** * @brief Returns a std::vector containing all labels for which min and max values (and indices) have been computed */ std::vector GetRelevantLabels() const { std::vector labels; for (auto&& it:m_LabelExtrema) { labels.push_back(it.first); } return labels; } IndexType GetMinIndex(LabelPixelType label) const { ExtremaMapTypeConstIterator it = m_LabelExtrema.find(label); if (it == m_LabelExtrema.end()) { MITK_ERROR << "invalid label"; } return (*it).second.m_MinIndex; } IndexType GetMaxIndex(LabelPixelType label) const { ExtremaMapTypeConstIterator it = m_LabelExtrema.find(label); if (it == m_LabelExtrema.end()) { MITK_ERROR << "invalid label"; } return (*it).second.m_MaxIndex; } PixelType GetGlobalMin() const { return m_GlobalMin; } PixelType GetGlobalMax() const { return m_GlobalMax; } IndexType GetGlobalMinIndex() const { return m_GlobalMinIndex; } IndexType GetGlobalMaxIndex() const { return m_GlobalMaxIndex; } /** Set the label image */ void SetLabelInput(const TLabelImage *input) { // Process object is not const-correct so the const casting is required. this->SetNthInput( 1, const_cast< TLabelImage * >( input ) ); } /** Get the label image */ const TLabelImage * GetLabelInput() const { return itkDynamicCastInDebugMode< TLabelImage * >( const_cast< DataObject * >( this->ProcessObject::GetInput(1) ) ); } protected: + MinMaxLabelImageFilterWithIndex() + { + this->DynamicMultiThreadingOff(); + } + void AllocateOutputs() override; void ThreadedGenerateData(const RegionType & outputRegionForThread, ThreadIdType threadId) override; void BeforeThreadedGenerateData() override; void AfterThreadedGenerateData() override; private: std::vector m_ThreadExtrema; ExtremaMapType m_LabelExtrema; PixelType m_GlobalMin; PixelType m_GlobalMax; IndexType m_GlobalMinIndex, m_GlobalMaxIndex; }; } #include "mitkMinMaxLabelmageFilterWithIndex.hxx" #endif diff --git a/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.hxx b/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.hxx index c1a0af7ae2..89dc9d1300 100644 --- a/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.hxx +++ b/Modules/ImageStatistics/mitkMinMaxLabelmageFilterWithIndex.hxx @@ -1,130 +1,130 @@ #ifndef MITK_MinMaxLabelImageFilterWithIndex_HXX #define MITK_MinMaxLabelImageFilterWithIndex_HXX #include #include namespace itk { template< typename TInputImage, typename TLabelImage > void MinMaxLabelImageFilterWithIndex< TInputImage, TLabelImage >::AllocateOutputs() { // Pass the input through as the output typename TInputImage::Pointer image = const_cast< TInputImage * >( this->GetInput() ); this->GraftOutput(image); // Nothing that needs to be allocated for the remaining outputs } template< typename TInputImage, typename TLabelImage > void MinMaxLabelImageFilterWithIndex< TInputImage, TLabelImage >::ThreadedGenerateData(const RegionType & outputRegionForThread, ThreadIdType threadId) { const SizeValueType size0 = outputRegionForThread.GetSize(0); if( size0 == 0) { return; } PixelType value; LabelPixelType label; ExtremaMapType threadExtrema; ExtremaMapTypeIterator threadExtremaIt; ImageRegionConstIteratorWithIndex< TInputImage > it (this->GetInput(), outputRegionForThread); ImageRegionConstIteratorWithIndex< TLabelImage > labelit (this->GetLabelInput(), outputRegionForThread); // do the work while ( !it.IsAtEnd() ) { value = it.Get(); label = labelit.Get(); threadExtremaIt = threadExtrema.find(label); // if label does not exist yet, create a new entry in the map. if (threadExtremaIt == threadExtrema.end()) { threadExtremaIt = threadExtrema.insert( MapValueType(label, LabelExtrema()) ).first; } if (value < (*threadExtremaIt).second.m_Min) { (*threadExtremaIt).second.m_Min = value; (*threadExtremaIt).second.m_MinIndex = it.GetIndex(); } if (value > (*threadExtremaIt).second.m_Max) { (*threadExtremaIt).second.m_Max = value; (*threadExtremaIt).second.m_MaxIndex = it.GetIndex(); } ++it; ++labelit; } m_ThreadExtrema[threadId] = threadExtrema; } template< typename TInputImage, typename TLabelImage > void MinMaxLabelImageFilterWithIndex< TInputImage, TLabelImage >::BeforeThreadedGenerateData() { - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); + ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits(); m_ThreadExtrema.resize(numberOfThreads); for (unsigned int i =0; i < numberOfThreads; i++) { m_ThreadExtrema[i] = ExtremaMapType(); } } template< typename TInputImage, typename TLabelImage > void MinMaxLabelImageFilterWithIndex< TInputImage, TLabelImage >::AfterThreadedGenerateData() { - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); + ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits(); m_GlobalMin = std::numeric_limits::max(); m_GlobalMax = std::numeric_limits::min(); ExtremaMapTypeIterator it; for (ThreadIdType i = 0; i < numberOfThreads; i++) { for (auto&& it2 : m_ThreadExtrema[i]) { it = m_LabelExtrema.find(it2.first); if (it == m_LabelExtrema.end()) { it = m_LabelExtrema.insert( MapValueType(it2.first, LabelExtrema()) ).first; } if (it2.second.m_Min < (*it).second.m_Min) { (*it).second.m_Min = it2.second.m_Min; (*it).second.m_MinIndex = it2.second.m_MinIndex; if (it2.second.m_Min < m_GlobalMin) { m_GlobalMin = it2.second.m_Min; m_GlobalMinIndex = it2.second.m_MinIndex; } } if (it2.second.m_Max > (*it).second.m_Max) { (*it).second.m_Max = it2.second.m_Max; (*it).second.m_MaxIndex = it2.second.m_MaxIndex; if (it2.second.m_Max > m_GlobalMax) { m_GlobalMax = it2.second.m_Max; m_GlobalMaxIndex = it2.second.m_MaxIndex; } } } } } } #endif diff --git a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp index 2663d77380..6acffbfdf4 100644 --- a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp +++ b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp @@ -1,512 +1,512 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkImageAccessByItk.h" #include #include #include #include #include #include #include #include -#include +#include #include #include #include #include #include #include #include namespace mitk { void PlanarFigureMaskGenerator::SetPlanarFigure(mitk::PlanarFigure::Pointer planarFigure) { if ( planarFigure.IsNull() ) { throw std::runtime_error( "Error: planar figure empty!" ); } const PlaneGeometry *planarFigurePlaneGeometry = planarFigure->GetPlaneGeometry(); if ( planarFigurePlaneGeometry == nullptr ) { throw std::runtime_error( "Planar-Figure not yet initialized!" ); } const auto *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry ); if ( planarFigureGeometry == nullptr ) { throw std::runtime_error( "Non-planar planar figures not supported!" ); } if (planarFigure != m_PlanarFigure) { this->Modified(); m_PlanarFigure = planarFigure; } } mitk::Image::ConstPointer PlanarFigureMaskGenerator::GetReferenceImage() { if (IsUpdateRequired()) { this->CalculateMask(); } return m_ReferenceImage; } template < typename TPixel, unsigned int VImageDimension > void PlanarFigureMaskGenerator::InternalCalculateMaskFromPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ) { typedef itk::Image< unsigned short, 2 > MaskImage2DType; typename MaskImage2DType::Pointer maskImage = MaskImage2DType::New(); maskImage->SetOrigin(image->GetOrigin()); maskImage->SetSpacing(image->GetSpacing()); maskImage->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); maskImage->SetBufferedRegion(image->GetBufferedRegion()); maskImage->SetDirection(image->GetDirection()); maskImage->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel()); maskImage->Allocate(); maskImage->FillBuffer(1); // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object. // These points are used by the vtkLassoStencilSource to create // a vtkImageStencil. const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const mitk::BaseGeometry *imageGeometry3D = m_inputImage->GetGeometry( 0 ); // If there is a second poly line in a closed planar figure, treat it as a hole. PlanarFigure::PolyLineType planarFigureHolePolyline; if (m_PlanarFigure->GetPolyLinesSize() == 2) planarFigureHolePolyline = m_PlanarFigure->GetPolyLine(1); // Determine x- and y-dimensions depending on principal axis // TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } // store the polyline contour as vtkPoints object vtkSmartPointer points = vtkSmartPointer::New(); for (const auto& point : planarFigurePolyline) { Point3D point3D; // Convert 2D point back to the local index coordinates of the selected image planarFigurePlaneGeometry->Map(point, point3D); imageGeometry3D->WorldToIndex(point3D, point3D); points->InsertNextPoint(point3D[i0], point3D[i1], 0); } vtkSmartPointer holePoints; if (!planarFigureHolePolyline.empty()) { holePoints = vtkSmartPointer::New(); Point3D point3D; for (const auto& point : planarFigureHolePolyline) { planarFigurePlaneGeometry->Map(point, point3D); imageGeometry3D->WorldToIndex(point3D, point3D); holePoints->InsertNextPoint(point3D[i0], point3D[i1], 0); } } // mark a malformed 2D planar figure ( i.e. area = 0 ) as out of bounds // this can happen when all control points of a rectangle lie on the same line = two of the three extents are zero double bounds[6] = {0}; points->GetBounds(bounds); bool extent_x = (fabs(bounds[0] - bounds[1])) < mitk::eps; bool extent_y = (fabs(bounds[2] - bounds[3])) < mitk::eps; bool extent_z = (fabs(bounds[4] - bounds[5])) < mitk::eps; // throw an exception if a closed planar figure is deformed, i.e. has only one non-zero extent if (m_PlanarFigure->IsClosed() && ((extent_x && extent_y) || (extent_x && extent_z) || (extent_y && extent_z))) { mitkThrow() << "Figure has a zero area and cannot be used for masking."; } // create a vtkLassoStencilSource and set the points of the Polygon vtkSmartPointer lassoStencil = vtkSmartPointer::New(); lassoStencil->SetShapeToPolygon(); lassoStencil->SetPoints(points); vtkSmartPointer holeLassoStencil = nullptr; if (holePoints.GetPointer() != nullptr) { holeLassoStencil = vtkSmartPointer::New(); holeLassoStencil->SetShapeToPolygon(); holeLassoStencil->SetPoints(holePoints); } // Export from ITK to VTK (to use a VTK filter) typedef itk::VTKImageImport< MaskImage2DType > ImageImportType; typedef itk::VTKImageExport< MaskImage2DType > ImageExportType; typename ImageExportType::Pointer itkExporter = ImageExportType::New(); itkExporter->SetInput( maskImage ); // itkExporter->SetInput( castFilter->GetOutput() ); vtkSmartPointer vtkImporter = vtkSmartPointer::New(); this->ConnectPipelines( itkExporter, vtkImporter ); // Apply the generated image stencil to the input image vtkSmartPointer imageStencilFilter = vtkSmartPointer::New(); imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); imageStencilFilter->SetStencilConnection(lassoStencil->GetOutputPort()); imageStencilFilter->ReverseStencilOff(); imageStencilFilter->SetBackgroundValue( 0 ); imageStencilFilter->Update(); vtkSmartPointer holeStencilFilter = nullptr; if (holeLassoStencil.GetPointer() != nullptr) { holeStencilFilter = vtkSmartPointer::New(); holeStencilFilter->SetInputConnection(imageStencilFilter->GetOutputPort()); holeStencilFilter->SetStencilConnection(holeLassoStencil->GetOutputPort()); holeStencilFilter->ReverseStencilOn(); holeStencilFilter->SetBackgroundValue(0); holeStencilFilter->Update(); } // Export from VTK back to ITK vtkSmartPointer vtkExporter = vtkSmartPointer::New(); vtkExporter->SetInputConnection( holeStencilFilter.GetPointer() == nullptr ? imageStencilFilter->GetOutputPort() : holeStencilFilter->GetOutputPort()); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); typedef itk::ImageDuplicator< ImageImportType::OutputImageType > DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( itkImporter->GetOutput() ); duplicator->Update(); // Store mask m_InternalITKImageMask2D = duplicator->GetOutput(); } template < typename TPixel, unsigned int VImageDimension > void PlanarFigureMaskGenerator::InternalCalculateMaskFromOpenPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ) { typedef itk::Image< unsigned short, 2 > MaskImage2DType; typedef itk::LineIterator< MaskImage2DType > LineIteratorType; typedef MaskImage2DType::IndexType IndexType2D; typedef std::vector< IndexType2D > IndexVecType; typename MaskImage2DType::Pointer maskImage = MaskImage2DType::New(); maskImage->SetOrigin(image->GetOrigin()); maskImage->SetSpacing(image->GetSpacing()); maskImage->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); maskImage->SetBufferedRegion(image->GetBufferedRegion()); maskImage->SetDirection(image->GetDirection()); maskImage->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel()); maskImage->Allocate(); maskImage->FillBuffer(0); // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object. const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const mitk::BaseGeometry *imageGeometry3D = m_inputImage->GetGeometry( 0 ); // Determine x- and y-dimensions depending on principal axis // TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } int numPolyLines = m_PlanarFigure->GetPolyLinesSize(); for ( int lineId = 0; lineId < numPolyLines; ++lineId ) { // store the polyline contour as vtkPoints object IndexVecType pointIndices; for(const auto& point : planarFigurePolyline) { Point3D point3D; planarFigurePlaneGeometry->Map(point, point3D); imageGeometry3D->WorldToIndex(point3D, point3D); IndexType2D index2D; index2D[0] = point3D[i0]; index2D[1] = point3D[i1]; pointIndices.push_back( index2D ); } size_t numLineSegments = pointIndices.size() - 1; for (size_t i = 0; i < numLineSegments; ++i) { LineIteratorType lineIt(maskImage, pointIndices[i], pointIndices[i+1]); while (!lineIt.IsAtEnd()) { lineIt.Set(1); ++lineIt; } } } // Store mask m_InternalITKImageMask2D = maskImage; } bool PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(const PlaneGeometry* planarGeometry, const BaseGeometry *geometry) { if (!planarGeometry) return false; if (!geometry) return false; unsigned int axis; return GetPrincipalAxis(geometry,planarGeometry->GetNormal(), axis); } bool PlanarFigureMaskGenerator::GetPrincipalAxis( const BaseGeometry *geometry, Vector3D vector, unsigned int &axis ) { vector.Normalize(); for ( unsigned int i = 0; i < 3; ++i ) { Vector3D axisVector = geometry->GetAxisVector( i ); axisVector.Normalize(); //normal mitk::eps is to pedantic for this check. See e.g. T27122 //therefore choose a larger epsilon. The value was set a) as small as //possible but b) still allowing to datasets like in (T27122) to pass //when floating rounding errors sum up. const double epsilon = 5e-5; if ( fabs( fabs( axisVector * vector ) - 1.0) < epsilon) { axis = i; return true; } } return false; } void PlanarFigureMaskGenerator::CalculateMask() { if (m_inputImage.IsNull()) { MITK_ERROR << "Image is not set."; } if (m_PlanarFigure.IsNull()) { MITK_ERROR << "PlanarFigure is not set."; } if (m_TimeStep != 0) { MITK_WARN << "Multiple TimeSteps are not supported in PlanarFigureMaskGenerator (yet)."; } const BaseGeometry *imageGeometry = m_inputImage->GetGeometry(); if ( imageGeometry == nullptr ) { throw std::runtime_error( "Image geometry invalid!" ); } if (m_inputImage->GetTimeSteps() > 0) { mitk::ImageTimeSelector::Pointer imgTimeSel = mitk::ImageTimeSelector::New(); imgTimeSel->SetInput(m_inputImage); imgTimeSel->SetTimeNr(m_TimeStep); imgTimeSel->UpdateLargestPossibleRegion(); m_InternalTimeSliceImage = imgTimeSel->GetOutput(); } else { m_InternalTimeSliceImage = m_inputImage; } m_InternalITKImageMask2D = nullptr; const PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const auto *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry ); //const BaseGeometry *imageGeometry = m_inputImage->GetGeometry(); // Find principal direction of PlanarFigure in input image unsigned int axis; if ( !this->GetPrincipalAxis( imageGeometry, planarFigureGeometry->GetNormal(), axis ) ) { throw std::runtime_error( "Non-aligned planar figures not supported!" ); } m_PlanarFigureAxis = axis; // Find slice number corresponding to PlanarFigure in input image itk::Image< unsigned short, 3 >::IndexType index; imageGeometry->WorldToIndex( planarFigureGeometry->GetOrigin(), index ); unsigned int slice = index[axis]; m_PlanarFigureSlice = slice; // extract image slice which corresponds to the planarFigure and store it in m_InternalImageSlice mitk::Image::ConstPointer inputImageSlice = extract2DImageSlice(axis, slice); //mitk::IOUtil::Save(inputImageSlice, "/home/fabian/inputSliceImage.nrrd"); // Compute mask from PlanarFigure // rastering for open planar figure: if ( !m_PlanarFigure->IsClosed() ) { AccessFixedDimensionByItk_1(inputImageSlice, InternalCalculateMaskFromOpenPlanarFigure, 2, axis) } else//for closed planar figure { AccessFixedDimensionByItk_1(inputImageSlice, InternalCalculateMaskFromPlanarFigure, 2, axis) } //convert itk mask to mitk::Image::Pointer and return it mitk::Image::Pointer planarFigureMaskImage; planarFigureMaskImage = mitk::GrabItkImageMemory(m_InternalITKImageMask2D); //mitk::IOUtil::Save(planarFigureMaskImage, "/home/fabian/planarFigureMaskImage.nrrd"); //Convert2Dto3DImageFilter::Pointer sliceTo3DImageConverter = Convert2Dto3DImageFilter::New(); //sliceTo3DImageConverter->SetInput(planarFigureMaskImage); //sliceTo3DImageConverter->Update(); //mitk::IOUtil::Save(sliceTo3DImageConverter->GetOutput(), "/home/fabian/3DsliceImage.nrrd"); m_ReferenceImage = inputImageSlice; //mitk::IOUtil::Save(m_ReferenceImage, "/home/fabian/referenceImage.nrrd"); m_InternalMask = planarFigureMaskImage; } void PlanarFigureMaskGenerator::SetTimeStep(unsigned int timeStep) { if (timeStep != m_TimeStep) { m_TimeStep = timeStep; } } mitk::Image::Pointer PlanarFigureMaskGenerator::GetMask() { if (IsUpdateRequired()) { this->CalculateMask(); this->Modified(); } m_InternalMaskUpdateTime = this->GetMTime(); return m_InternalMask; } mitk::Image::ConstPointer PlanarFigureMaskGenerator::extract2DImageSlice(unsigned int axis, unsigned int slice) { // Extract slice with given position and direction from image unsigned int dimension = m_InternalTimeSliceImage->GetDimension(); if (dimension == 3) { ExtractImageFilter::Pointer imageExtractor = ExtractImageFilter::New(); imageExtractor->SetInput( m_InternalTimeSliceImage ); imageExtractor->SetSliceDimension( axis ); imageExtractor->SetSliceIndex( slice ); imageExtractor->Update(); return imageExtractor->GetOutput(); } else if(dimension == 2) { return m_InternalTimeSliceImage; } else { MITK_ERROR << "Unsupported image dimension. Dimension is: " << dimension << ". Only 2D and 3D images are supported."; return nullptr; } } bool PlanarFigureMaskGenerator::IsUpdateRequired() const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long internalMaskTimeStamp = m_InternalMask->GetMTime(); unsigned long planarFigureTimeStamp = m_PlanarFigure->GetMTime(); unsigned long inputImageTimeStamp = m_inputImage->GetMTime(); if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed { return true; } if (m_InternalMaskUpdateTime < planarFigureTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class { return true; } if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class { return true; } return false; } } diff --git a/Modules/ImageStatistics/mitkStatisticsImageFilter.h b/Modules/ImageStatistics/mitkStatisticsImageFilter.h new file mode 100644 index 0000000000..54abfc4cf3 --- /dev/null +++ b/Modules/ImageStatistics/mitkStatisticsImageFilter.h @@ -0,0 +1,143 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +// This file is based on ITK's itkStatisticsImageFilter.h + +#ifndef mitkStatisticsImageFilter +#define mitkStatisticsImageFilter + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace mitk +{ + template + class StatisticsImageFilter : public itk::ImageSink + { + public: + using Self = StatisticsImageFilter; + using Superclass = itk::ImageSink; + using Pointer = itk::SmartPointer; + using ConstPointer = itk::SmartPointer; + + itkFactorylessNewMacro(Self); + + itkTypeMacro(StatisticsImageFilter, itk::ImageSink); + + using RegionType = typename TInputImage::RegionType; + using PixelType = typename TInputImage::PixelType; + + using RealType = typename itk::NumericTraits::RealType; + + using HistogramType = typename itk::Statistics::Histogram; + using HistogramPointer = itk::SmartPointer; + + using DataObjectPointer = typename itk::DataObject::Pointer; + + template + using SimpleDataObjectDecorator = itk::SimpleDataObjectDecorator; + + using RealObjectType = SimpleDataObjectDecorator; + using PixelObjectType = SimpleDataObjectDecorator; + using ProcessObject = itk::ProcessObject; + + itkGetDecoratedOutputMacro(Minimum, PixelType); + itkGetDecoratedOutputMacro(Maximum, PixelType); + itkGetDecoratedOutputMacro(Mean, RealType); + itkGetDecoratedOutputMacro(Sigma, RealType); + itkGetDecoratedOutputMacro(Variance, RealType); + itkGetDecoratedOutputMacro(Sum, RealType); + itkGetDecoratedOutputMacro(SumOfSquares, RealType); + itkGetDecoratedOutputMacro(SumOfCubes, RealType); + itkGetDecoratedOutputMacro(SumOfQuadruples, RealType); + itkGetDecoratedOutputMacro(Skewness, RealType); + itkGetDecoratedOutputMacro(Kurtosis, RealType); + itkGetDecoratedOutputMacro(MPP, RealType); + itkGetDecoratedOutputMacro(Histogram, HistogramPointer); + itkGetDecoratedOutputMacro(Entropy, RealType); + itkGetDecoratedOutputMacro(Uniformity, RealType); + itkGetDecoratedOutputMacro(UPP, RealType); + itkGetDecoratedOutputMacro(Median, RealType); + + void SetHistogramParameters(unsigned int size, RealType lowerBound, RealType upperBound); + + using DataObjectIdentifierType = itk::ProcessObject::DataObjectIdentifierType; + using Superclass::MakeOutput; + + /** Make a DataObject of the correct type to be used as the specified output. */ + DataObjectPointer MakeOutput(const DataObjectIdentifierType& name) override; + + protected: + StatisticsImageFilter(); + ~StatisticsImageFilter(); + + itkSetDecoratedOutputMacro(Minimum, PixelType); + itkSetDecoratedOutputMacro(Maximum, PixelType); + itkSetDecoratedOutputMacro(Mean, RealType); + itkSetDecoratedOutputMacro(Sigma, RealType); + itkSetDecoratedOutputMacro(Variance, RealType); + itkSetDecoratedOutputMacro(Sum, RealType); + itkSetDecoratedOutputMacro(SumOfSquares, RealType); + itkSetDecoratedOutputMacro(SumOfCubes, RealType); + itkSetDecoratedOutputMacro(SumOfQuadruples, RealType); + itkSetDecoratedOutputMacro(Skewness, RealType); + itkSetDecoratedOutputMacro(Kurtosis, RealType); + itkSetDecoratedOutputMacro(MPP, RealType); + itkSetDecoratedOutputMacro(Histogram, HistogramPointer); + itkSetDecoratedOutputMacro(Entropy, RealType); + itkSetDecoratedOutputMacro(Uniformity, RealType); + itkSetDecoratedOutputMacro(UPP, RealType); + itkSetDecoratedOutputMacro(Median, RealType); + + void BeforeStreamedGenerateData() override; + void ThreadedStreamedGenerateData(const RegionType&) override; + void AfterStreamedGenerateData() override; + + void PrintSelf(std::ostream& os, itk::Indent indent) const override; + + private: + HistogramPointer CreateInitializedHistogram() const; + + bool m_ComputeHistogram; + unsigned int m_HistogramSize; + RealType m_HistogramLowerBound; + RealType m_HistogramUpperBound; + HistogramPointer m_Histogram; + + itk::CompensatedSummation m_Sum; + itk::CompensatedSummation m_SumOfPositivePixels; + itk::CompensatedSummation m_SumOfSquares; + itk::CompensatedSummation m_SumOfCubes; + itk::CompensatedSummation m_SumOfQuadruples; + + itk::SizeValueType m_Count; + itk::SizeValueType m_CountOfPositivePixels; + PixelType m_Min; + PixelType m_Max; + + std::mutex m_Mutex; + }; +} + +#ifndef ITK_MANUAL_INSTANTIATION +#include +#endif + +#endif diff --git a/Modules/ImageStatistics/mitkStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkStatisticsImageFilter.hxx new file mode 100644 index 0000000000..8c44f722dc --- /dev/null +++ b/Modules/ImageStatistics/mitkStatisticsImageFilter.hxx @@ -0,0 +1,325 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +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 mitkStatisticsImageFilter_hxx +#define mitkStatisticsImageFilter_hxx + +#include +#include +#include + +template +mitk::StatisticsImageFilter::StatisticsImageFilter() + : m_ComputeHistogram(false), + m_HistogramSize(0), + m_HistogramLowerBound(itk::NumericTraits::NonpositiveMin()), + m_HistogramUpperBound(itk::NumericTraits::max()), + m_Sum(1), + m_SumOfPositivePixels(1), + m_SumOfSquares(1), + m_SumOfCubes(1), + m_SumOfQuadruples(1), + m_Count(1), + m_CountOfPositivePixels(1), + m_Min(1), + m_Max(1) +{ + this->SetNumberOfRequiredInputs(1); + + this->SetMinimum(itk::NumericTraits::max()); + this->SetMaximum(itk::NumericTraits::NonpositiveMin()); + this->SetMean(itk::NumericTraits::max()); + this->SetSigma(itk::NumericTraits::max()); + this->SetVariance(itk::NumericTraits::max()); + this->SetSum(0); + this->SetSumOfSquares(0); + this->SetSumOfCubes(0); + this->SetSumOfQuadruples(0); + this->SetSkewness(0); + this->SetKurtosis(0); + this->SetMPP(0); + this->SetEntropy(-1.0); + this->SetUniformity(0); + this->SetUPP(0); + this->SetMedian(0); +} + +template +mitk::StatisticsImageFilter::~StatisticsImageFilter() +{ +} + +template +typename mitk::StatisticsImageFilter::DataObjectPointer mitk::StatisticsImageFilter::MakeOutput(const DataObjectIdentifierType& name) +{ + if (name == "Minimum" || + name == "Maximum") + { + return PixelObjectType::New(); + } + + if (name == "Mean" || + name == "Sigma" || + name == "Variance" || + name == "Sum" || + name == "SumOfSquares" || + name == "SumOfCubes" || + name == "SumOfQuadruples" || + name == "Skewness" || + name == "Kurtosis" || + name == "MPP" || + name == "Entropy" || + name == "Uniformity" || + name == "UPP" || + name == "Median") + { + return RealObjectType::New(); + } + + if (name == "Histogram") + { + return HistogramType::New(); + } + + return Superclass::MakeOutput(name); +} + +template +void mitk::StatisticsImageFilter::SetHistogramParameters(unsigned int size, RealType lowerBound, RealType upperBound) +{ + bool modified = false; + + if (m_HistogramSize != size) + { + m_HistogramSize = size; + modified = true; + } + + if (m_HistogramLowerBound != lowerBound) + { + m_HistogramLowerBound = lowerBound; + modified = true; + } + + if (m_HistogramUpperBound != upperBound) + { + m_HistogramUpperBound = upperBound; + modified = true; + } + + m_ComputeHistogram = true; + + if (modified) + this->Modified(); +} + +template +auto mitk::StatisticsImageFilter::CreateInitializedHistogram() const -> HistogramPointer +{ + typename HistogramType::SizeType size; + size.SetSize(1); + size.Fill(m_HistogramSize); + + typename HistogramType::MeasurementVectorType lowerBound; + lowerBound.SetSize(1); + lowerBound.Fill(m_HistogramLowerBound); + + typename HistogramType::MeasurementVectorType upperBound; + upperBound.SetSize(1); + upperBound.Fill(m_HistogramUpperBound); + + auto histogram = HistogramType::New(); + histogram->SetMeasurementVectorSize(1); + histogram->Initialize(size, lowerBound, upperBound); + + return histogram; +} + +template +void mitk::StatisticsImageFilter::BeforeStreamedGenerateData() +{ + Superclass::BeforeStreamedGenerateData(); + + m_Sum = 0; + m_SumOfPositivePixels = 0; + m_SumOfSquares = 0; + m_SumOfCubes = 0; + m_SumOfQuadruples = 0; + m_Count = 0; + m_CountOfPositivePixels = 0; + m_Min = itk::NumericTraits::max(); + m_Max = itk::NumericTraits::NonpositiveMin(); + + if (m_ComputeHistogram) + m_Histogram = this->CreateInitializedHistogram(); +} + +template +void mitk::StatisticsImageFilter::ThreadedStreamedGenerateData(const RegionType& regionForThread) +{ + itk::CompensatedSummation sum = 0; + itk::CompensatedSummation sumOfPositivePixels = 0; + itk::CompensatedSummation sumOfSquares = 0; + itk::CompensatedSummation sumOfCubes = 0; + itk::CompensatedSummation sumOfQuadruples = 0; + itk::SizeValueType count = 0; + itk::SizeValueType countOfPositivePixels = 0; + auto min = itk::NumericTraits::max(); + auto max = itk::NumericTraits::NonpositiveMin(); + RealType realValue = 0; + RealType squareValue = 0; + + HistogramPointer histogram; + typename HistogramType::MeasurementVectorType histogramMeasurement; + typename HistogramType::IndexType histogramIndex; + + if (m_ComputeHistogram) // Initialize histogram + { + histogram = this->CreateInitializedHistogram(); + histogramMeasurement.SetSize(1); + } + + itk::ImageScanlineConstIterator it(this->GetInput(), regionForThread); + + while (!it.IsAtEnd()) + { + while (!it.IsAtEndOfLine()) + { + const auto& value = it.Get(); + realValue = static_cast(value); + + if (m_ComputeHistogram) // Compute histogram + { + histogramMeasurement[0] = realValue; + histogram->GetIndex(histogramMeasurement, histogramIndex); + histogram->IncreaseFrequencyOfIndex(histogramIndex, 1); + } + + min = std::min(min, value); + max = std::max(max, value); + squareValue = realValue * realValue; + + sum += realValue; + sumOfSquares += squareValue; + sumOfCubes += squareValue * realValue; + sumOfQuadruples += squareValue * squareValue; + ++count; + + if (0 < realValue) + { + sumOfPositivePixels += realValue; + ++countOfPositivePixels; + } + + ++it; + } + + it.NextLine(); + } + + std::lock_guard mutexHolder(m_Mutex); + + if (m_ComputeHistogram) // Merge histograms + { + typename HistogramType::ConstIterator histogramIt = histogram->Begin(); + typename HistogramType::ConstIterator histogramEnd = histogram->End(); + + while (histogramIt != histogramEnd) + { + m_Histogram->GetIndex(histogramIt.GetMeasurementVector(), histogramIndex); + m_Histogram->IncreaseFrequencyOfIndex(histogramIndex, histogramIt.GetFrequency()); + ++histogramIt; + } + } + + m_Sum += sum; + m_SumOfPositivePixels += sumOfPositivePixels; + m_SumOfSquares += sumOfSquares; + m_SumOfCubes += sumOfCubes; + m_SumOfQuadruples += sumOfQuadruples; + m_Count += count; + m_CountOfPositivePixels += countOfPositivePixels; + m_Min = std::min(min, m_Min); + m_Max = std::max(max, m_Max); +} + +template +void mitk::StatisticsImageFilter::AfterStreamedGenerateData() +{ + Superclass::AfterStreamedGenerateData(); + + const RealType sum = m_Sum.GetSum(); + const RealType sumOfPositivePixels = m_SumOfPositivePixels.GetSum(); + const RealType sumOfSquares = m_SumOfSquares.GetSum(); + const RealType sumOfCubes = m_SumOfCubes.GetSum(); + const RealType sumOfQuadruples = m_SumOfQuadruples.GetSum(); + const itk::SizeValueType count = m_Count; + const itk::SizeValueType countOfPositivePixels = m_CountOfPositivePixels; + const PixelType minimum = m_Min; + const PixelType maximum = m_Max; + + const RealType mean = sum / static_cast(count); + const RealType variance = (sumOfSquares - (sum * sum / static_cast(count))) / (static_cast(count) - 1); + const RealType sigma = std::sqrt(variance); + + const RealType secondMoment = sumOfSquares / static_cast(count); + const RealType thirdMoment = sumOfCubes / static_cast(count); + const RealType fourthMoment = sumOfQuadruples / static_cast(count); + const RealType skewness = (thirdMoment - 3 * secondMoment * mean + 2 * std::pow(mean, 3)) / std::pow(secondMoment - std::pow(mean, 2), 1.5); + const RealType kurtosis = (fourthMoment - 4 * thirdMoment * mean + 6 * secondMoment * std::pow(mean, 2) - 3 * std::pow(mean, 4)) / std::pow(secondMoment - std::pow(mean, 2), 2); + const RealType meanOfPositivePixels = sumOfPositivePixels / static_cast(countOfPositivePixels); + + this->SetMinimum(minimum); + this->SetMaximum(maximum); + this->SetMean(mean); + this->SetSigma(sigma); + this->SetVariance(variance); + this->SetSum(sum); + this->SetSumOfSquares(sumOfSquares); + this->SetSumOfCubes(sumOfCubes); + this->SetSumOfQuadruples(sumOfQuadruples); + this->SetSkewness(skewness); + this->SetKurtosis(kurtosis); + this->SetMPP(meanOfPositivePixels); + + if (m_ComputeHistogram) + { + this->SetHistogram(m_Histogram); + + mitk::HistogramStatisticsCalculator histogramStatisticsCalculator; + histogramStatisticsCalculator.SetHistogram(m_Histogram); + histogramStatisticsCalculator.CalculateStatistics(); + + this->SetEntropy(histogramStatisticsCalculator.GetEntropy()); + this->SetUniformity(histogramStatisticsCalculator.GetUniformity()); + this->SetUPP(histogramStatisticsCalculator.GetUPP()); + this->SetMedian(histogramStatisticsCalculator.GetMedian()); + } +} + +template +void mitk::StatisticsImageFilter::PrintSelf(std::ostream& os, itk::Indent indent) const +{ + Superclass::PrintSelf(os, indent); + + os << indent << "SumOfCubes: " << this->GetSumOfCubes() << std::endl; + os << indent << "SumOfQuadruples: " << this->GetSumOfQuadruples() << std::endl; + os << indent << "Skewness: " << this->GetSkewness() << std::endl; + os << indent << "Kurtosis: " << this->GetKurtosis() << std::endl; + os << indent << "MPP: " << this->GetMPP() << std::endl; + os << indent << "Entropy: " << this->GetEntropy() << std::endl; + os << indent << "Uniformity: " << this->GetUniformity() << std::endl; + os << indent << "UPP: " << this->GetUPP() << std::endl; + os << indent << "Median: " << this->GetMedian() << std::endl; +} + +#endif diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp index 56bb7ac961..738628cf24 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp @@ -1,425 +1,424 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "QmitkImageStatisticsTreeModel.h" #include "QmitkImageStatisticsTreeItem.h" -#include "itkMutexLockHolder.h" #include "mitkImageStatisticsContainerManager.h" #include "mitkProportionalTimeGeometry.h" #include "mitkStatisticsToImageRelationRule.h" #include "mitkStatisticsToMaskRelationRule.h" #include "QmitkStyleManager.h" QmitkImageStatisticsTreeModel::QmitkImageStatisticsTreeModel(QObject *parent) : QmitkAbstractDataStorageModel(parent) { m_RootItem = new QmitkImageStatisticsTreeItem(); } QmitkImageStatisticsTreeModel ::~QmitkImageStatisticsTreeModel() { // set data storage to nullptr so that the event listener gets removed this->SetDataStorage(nullptr); delete m_RootItem; }; void QmitkImageStatisticsTreeModel::DataStorageChanged() { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } void QmitkImageStatisticsTreeModel::NodePredicateChanged() { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } int QmitkImageStatisticsTreeModel::columnCount(const QModelIndex& /*parent*/) const { int columns = m_StatisticNames.size() + 1; return columns; } int QmitkImageStatisticsTreeModel::rowCount(const QModelIndex &parent) const { QmitkImageStatisticsTreeItem *parentItem; if (parent.column() > 0) return 0; if (!parent.isValid()) parentItem = m_RootItem; else parentItem = static_cast(parent.internalPointer()); return parentItem->childCount(); } QVariant QmitkImageStatisticsTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); QmitkImageStatisticsTreeItem* item = static_cast(index.internalPointer()); if (role == Qt::DisplayRole) { return item->data(index.column()); } else if (role == Qt::DecorationRole && index.column() == 0 && item->isWIP() && item->childCount()==0) { return QVariant(QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/hourglass-half-solid.svg"))); } return QVariant(); } QModelIndex QmitkImageStatisticsTreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); QmitkImageStatisticsTreeItem *parentItem; if (!parent.isValid()) parentItem = m_RootItem; else parentItem = static_cast(parent.internalPointer()); QmitkImageStatisticsTreeItem *childItem = parentItem->child(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } QModelIndex QmitkImageStatisticsTreeModel::parent(const QModelIndex &child) const { if (!child.isValid()) return QModelIndex(); QmitkImageStatisticsTreeItem *childItem = static_cast(child.internalPointer()); QmitkImageStatisticsTreeItem *parentItem = childItem->parentItem(); if (parentItem == m_RootItem) return QModelIndex(); return createIndex(parentItem->row(), 0, parentItem); } Qt::ItemFlags QmitkImageStatisticsTreeModel::flags(const QModelIndex &index) const { if (!index.isValid()) return nullptr; return QAbstractItemModel::flags(index); } QVariant QmitkImageStatisticsTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole == role) && (Qt::Horizontal == orientation)) { if (section == 0) { return m_HeaderFirstColumn; } else { return QVariant(m_StatisticNames.at(section - 1).c_str()); } } return QVariant(); } void QmitkImageStatisticsTreeModel::SetImageNodes(const std::vector &nodes) { std::vector> tempNodes; for (const auto &node : nodes) { auto data = node->GetData(); if (data) { auto timeSteps = data->GetTimeSteps(); for (unsigned int i = 0; i < timeSteps; i++) { tempNodes.push_back(std::make_pair(node, i)); } } } emit beginResetModel(); m_TimeStepResolvedImageNodes = std::move(tempNodes); m_ImageNodes = nodes; UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } void QmitkImageStatisticsTreeModel::SetMaskNodes(const std::vector &nodes) { std::vector> tempNodes; for (const auto &node : nodes) { auto data = node->GetData(); if (data) { auto timeSteps = data->GetTimeSteps(); // special case: apply one mask to each timestep of an 4D image if (timeSteps == 1 && m_TimeStepResolvedImageNodes.size() > 1) { timeSteps = m_TimeStepResolvedImageNodes.size(); } for (unsigned int i = 0; i < timeSteps; i++) { tempNodes.push_back(std::make_pair(node, i)); } } } emit beginResetModel(); m_TimeStepResolvedMaskNodes = std::move(tempNodes); m_MaskNodes = nodes; UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } void QmitkImageStatisticsTreeModel::Clear() { emit beginResetModel(); m_Statistics.clear(); m_ImageNodes.clear(); m_TimeStepResolvedImageNodes.clear(); m_MaskNodes.clear(); m_StatisticNames.clear(); emit endResetModel(); emit modelChanged(); } void QmitkImageStatisticsTreeModel::SetIgnoreZeroValueVoxel(bool _arg) { if (m_IgnoreZeroValueVoxel != _arg) { emit beginResetModel(); m_IgnoreZeroValueVoxel = _arg; UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } } bool QmitkImageStatisticsTreeModel::GetIgnoreZeroValueVoxel() const { return this->m_IgnoreZeroValueVoxel; } void QmitkImageStatisticsTreeModel::SetHistogramNBins(unsigned int nbins) { if (m_HistogramNBins != nbins) { emit beginResetModel(); m_HistogramNBins = nbins; UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } } unsigned int QmitkImageStatisticsTreeModel::GetHistogramNBins() const { return this->m_HistogramNBins; } void QmitkImageStatisticsTreeModel::UpdateByDataStorage() { StatisticsContainerVector newStatistics; auto datamanager = m_DataStorage.Lock(); if (datamanager.IsNotNull()) { for (const auto &image : m_ImageNodes) { if (m_MaskNodes.empty()) { auto stats = mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), nullptr, m_IgnoreZeroValueVoxel, m_HistogramNBins, true, false); if (stats.IsNotNull()) { newStatistics.emplace_back(stats); } } else { for (const auto &mask : m_MaskNodes) { auto stats = mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), mask->GetData(), m_IgnoreZeroValueVoxel, m_HistogramNBins, true, false); if (stats.IsNotNull()) { newStatistics.emplace_back(stats); } } } } if (!newStatistics.empty()) { emit dataAvailable(); } } { - itk::MutexLockHolder locked(m_Mutex); + std::lock_guard locked(m_Mutex); m_Statistics = newStatistics; } m_StatisticNames = mitk::GetAllStatisticNames(m_Statistics); BuildHierarchicalModel(); } void QmitkImageStatisticsTreeModel::BuildHierarchicalModel() { // reset old model delete m_RootItem; m_RootItem = new QmitkImageStatisticsTreeItem(); bool hasMask = false; bool hasMultipleTimesteps = false; std::map dataNodeToTreeItem; for (const auto &statistic : m_Statistics) { bool isWIP = statistic->GetProperty(mitk::STATS_GENERATION_STATUS_PROPERTY_NAME.c_str()).IsNotNull(); // get the connected image data node/mask data node auto imageRule = mitk::StatisticsToImageRelationRule::New(); auto imageOfStatisticsPredicate = imageRule->GetDestinationsDetector(statistic); auto imageFinding = std::find_if(m_ImageNodes.begin(), m_ImageNodes.end(), [&imageOfStatisticsPredicate](const mitk::DataNode::ConstPointer& testNode) { return imageOfStatisticsPredicate->CheckNode(testNode); }); auto maskRule = mitk::StatisticsToMaskRelationRule::New(); auto maskOfStatisticsPredicate = maskRule->GetDestinationsDetector(statistic); auto maskFinding = std::find_if(m_MaskNodes.begin(), m_MaskNodes.end(), [&maskOfStatisticsPredicate](const mitk::DataNode::ConstPointer& testNode) { return maskOfStatisticsPredicate->CheckNode(testNode); }); if (imageFinding == m_ImageNodes.end()) { mitkThrow() << "no image found connected to statistic" << statistic << " Aborting."; } auto& image = *imageFinding; // image: 1. hierarchy level QmitkImageStatisticsTreeItem *imageItem = nullptr; auto search = dataNodeToTreeItem.find(image); // the tree item was created previously if (search != dataNodeToTreeItem.end()) { imageItem = search->second; } // create the tree item else { QString imageLabel = QString::fromStdString(image->GetName()); if (statistic->GetTimeSteps() == 1 && maskFinding == m_MaskNodes.end()) { auto statisticsObject = statistic->GetStatisticsForTimeStep(0); imageItem = new QmitkImageStatisticsTreeItem(statisticsObject, m_StatisticNames, imageLabel, isWIP, m_RootItem); } else { imageItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, imageLabel, isWIP, m_RootItem); } m_RootItem->appendChild(imageItem); dataNodeToTreeItem.emplace(image, imageItem); } // mask: 2. hierarchy level (optional, only if mask exists) QmitkImageStatisticsTreeItem *lastParent = nullptr; if (maskFinding != m_MaskNodes.end()) { auto& mask = *maskFinding; QString maskLabel = QString::fromStdString(mask->GetName()); QmitkImageStatisticsTreeItem *maskItem; // add statistical values directly in this hierarchy level if (statistic->GetTimeSteps() == 1) { auto statisticsObject = statistic->GetStatisticsForTimeStep(0); maskItem = new QmitkImageStatisticsTreeItem(statisticsObject, m_StatisticNames, maskLabel, isWIP, imageItem); } else { maskItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, maskLabel, isWIP, imageItem); } imageItem->appendChild(maskItem); lastParent = maskItem; hasMask = true; } else { lastParent = imageItem; } // 3. hierarchy level (optional, only if >1 timestep) if (statistic->GetTimeSteps() > 1) { for (unsigned int i = 0; i < statistic->GetTimeSteps(); i++) { QString timeStepLabel = "[" + QString::number(i) + "] " + QString::number(statistic->GetTimeGeometry()->TimeStepToTimePoint(i)) + " ms"; if (statistic->TimeStepExists(i)) { auto statisticsItem = new QmitkImageStatisticsTreeItem( statistic->GetStatisticsForTimeStep(i), m_StatisticNames, timeStepLabel, isWIP, lastParent); lastParent->appendChild(statisticsItem); } } hasMultipleTimesteps = true; } } QString headerString = "Images"; if (hasMask) { headerString += "/Masks"; } if (hasMultipleTimesteps) { headerString += "/Timesteps"; } m_HeaderFirstColumn = headerString; } void QmitkImageStatisticsTreeModel::NodeRemoved(const mitk::DataNode *) { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } void QmitkImageStatisticsTreeModel::NodeAdded(const mitk::DataNode *) { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } void QmitkImageStatisticsTreeModel::NodeChanged(const mitk::DataNode *) { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); emit modelChanged(); } diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h index 800959cba8..1d406558cc 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h @@ -1,127 +1,127 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 QmitkImageStatisticsTreeModel_h #define QmitkImageStatisticsTreeModel_h -#include "itkSimpleFastMutexLock.h" - #include "QmitkAbstractDataStorageModel.h" //MITK #include #include "mitkImageStatisticsContainer.h" +#include + class QmitkImageStatisticsTreeItem; /*! \class QmitkImageStatisticsTreeModel The class is used to represent the information of mitk::ImageStatisticsContainer in the set datastorage in the context of the QT view-model-concept. The represented ImageStatisticContainer are specified by setting the image and mask nodes that should be regarded. In addition you may specified the statistic computation property HistorgramNBins and IgnoreZeroValueVoxel to select the correct statistics. */ class MITKIMAGESTATISTICSUI_EXPORT QmitkImageStatisticsTreeModel : public QmitkAbstractDataStorageModel { Q_OBJECT public: QmitkImageStatisticsTreeModel(QObject *parent = nullptr); ~QmitkImageStatisticsTreeModel() override; void SetImageNodes(const std::vector& nodes); void SetMaskNodes(const std::vector& nodes); void Clear(); /*! /brief Set flag to ignore zero valued voxels */ void SetIgnoreZeroValueVoxel(bool _arg); /*! /brief Get status of zero value voxel ignoring. */ bool GetIgnoreZeroValueVoxel() const; /*! /brief Set bin size for histogram resolution.*/ void SetHistogramNBins(unsigned int nbins); /*! /brief Get bin size for histogram resolution.*/ unsigned int GetHistogramNBins() const; Qt::ItemFlags flags(const QModelIndex &index) const override; QVariant data(const QModelIndex &index, int role) const override; QVariant headerData(int section, Qt::Orientation orientation, int role) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; signals: void dataAvailable(); /** Is emitted whenever the model changes are finished (usually a bit later than dataAvailable()).*/ void modelChanged(); protected: /* * @brief See 'QmitkAbstractDataStorageModel' */ void DataStorageChanged() override; /* * @brief See 'QmitkAbstractDataStorageModel' */ void NodePredicateChanged() override; /* * @brief See 'QmitkAbstractDataStorageModel' */ void NodeAdded(const mitk::DataNode *node) override; /* * @brief See 'QmitkAbstractDataStorageModel' */ void NodeChanged(const mitk::DataNode *node) override; /* * @brief See 'QmitkAbstractDataStorageModel' */ void NodeRemoved(const mitk::DataNode *node) override; private: void UpdateByDataStorage(); using StatisticsContainerVector = std::vector; /* builds a hierarchical tree model for the image statistics 1. Level: Image --> 2. Level: Mask [if exist] --> 3. Level: Timestep [if >1 exist] */ void BuildHierarchicalModel(); StatisticsContainerVector m_Statistics; /** Relevant images set by the user.*/ std::vector m_ImageNodes; /** Helper that is constructed when m_ImageNodes is set. It has the same order like m_ImageNodes, but each image is represented n times, while n is the number of time steps the respective image has. This structure makes the business logic to select the correct image given a QIndex much simpler and therefore easy to understand/maintain. */ std::vector > m_TimeStepResolvedImageNodes; /** relevant masks set by the user.*/ std::vector m_MaskNodes; /** @sa m_TimeStepResolvedImageNodes */ std::vector> m_TimeStepResolvedMaskNodes; std::vector m_StatisticNames; - itk::SimpleFastMutexLock m_Mutex; + std::mutex m_Mutex; QmitkImageStatisticsTreeItem *m_RootItem; QVariant m_HeaderFirstColumn; bool m_IgnoreZeroValueVoxel = false; unsigned int m_HistogramNBins = 100; }; #endif // mitkQmitkImageStatisticsTreeModel_h diff --git a/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp b/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp index df9e2f4846..4562aed61a 100644 --- a/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp +++ b/Modules/ImageStatisticsUI/test/QmitkImageStatisticsDataGeneratorTest.cpp @@ -1,519 +1,537 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "QmitkImageStatisticsDataGenerator.h" #include #include #include "mitkImage.h" #include "mitkPlanarFigure.h" #include "mitkIOUtil.h" #include "mitkStatisticsToImageRelationRule.h" #include "mitkStatisticsToMaskRelationRule.h" #include "mitkImageStatisticsContainerManager.h" #include "mitkProperties.h" #include "QmitkImageStatisticsCalculationRunnable.h" #include #include class TestQmitkImageStatisticsDataGenerator : public QmitkImageStatisticsDataGenerator { public: TestQmitkImageStatisticsDataGenerator(mitk::DataStorage::Pointer storage, QObject* parent = nullptr) : QmitkImageStatisticsDataGenerator(storage, parent) { connect(this, &QmitkDataGeneratorBase::NewDataAvailable, this, &TestQmitkImageStatisticsDataGenerator::NewDataAvailableEmited); connect(this, &QmitkDataGeneratorBase::DataGenerationStarted, this, &TestQmitkImageStatisticsDataGenerator::DataGenerationStartedEmited); connect(this, &QmitkDataGeneratorBase::GenerationFinished, this, &TestQmitkImageStatisticsDataGenerator::GenerationFinishedEmited); connect(this, &QmitkDataGeneratorBase::JobError, this, &TestQmitkImageStatisticsDataGenerator::JobErrorEmited); }; mutable std::vector m_NewDataAvailable; void NewDataAvailableEmited(mitk::DataStorage::SetOfObjects::ConstPointer data) const { m_NewDataAvailable.emplace_back(data); }; mutable int m_DataGenerationStartedEmited = 0; void DataGenerationStartedEmited(const mitk::DataNode* /*imageNode*/, const mitk::DataNode* /*roiNode*/, const QmitkDataGenerationJobBase* /*job*/) const { m_DataGenerationStartedEmited++; } mutable int m_GenerationFinishedEmited = 0; void GenerationFinishedEmited() const { m_GenerationFinishedEmited++; QCoreApplication::instance()->quit(); } mutable std::vector m_JobErrorEmited_error; void JobErrorEmited(QString error, const QmitkDataGenerationJobBase* /*failedJob*/) const { m_JobErrorEmited_error.emplace_back(error); } }; class QmitkImageStatisticsDataGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(QmitkImageStatisticsDataGeneratorTestSuite); MITK_TEST(GetterSetterTest); MITK_TEST(NullTest); MITK_TEST(OneImageTest); MITK_TEST(MultiImageTest); MITK_TEST(ImageAndROITest); MITK_TEST(ImageAndMultiROITest); MITK_TEST(MultiMultiTest); MITK_TEST(InputChangedTest); MITK_TEST(SettingsChangedTest); MITK_TEST(DataStorageModificationTest); CPPUNIT_TEST_SUITE_END(); mitk::DataStorage::Pointer m_DataStorage; mitk::DataNode::Pointer m_ImageNode1; mitk::DataNode::Pointer m_ImageNode2; mitk::DataNode::Pointer m_MaskImageNode; mitk::DataNode::Pointer m_PFNode; mitk::Image::Pointer m_Image1; mitk::Image::Pointer m_Image2; mitk::Image::Pointer m_Mask; mitk::PlanarFigure::Pointer m_PF; QCoreApplication* m_TestApp; public: void setUp() override { m_DataStorage = mitk::StandaloneDataStorage::New(); m_ImageNode1 = mitk::DataNode::New(); m_ImageNode1->SetName("Image_1"); auto pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); m_Image1 = mitk::IOUtil::Load(pic3DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Image1.IsNotNull()); m_ImageNode1->SetData(m_Image1); m_DataStorage->Add(m_ImageNode1); m_ImageNode2 = mitk::DataNode::New(); m_ImageNode2->SetName("Image_2"); m_Image2 = mitk::IOUtil::Load(pic3DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Image2.IsNotNull()); m_ImageNode2->SetData(m_Image2); m_DataStorage->Add(m_ImageNode2); m_MaskImageNode = mitk::DataNode::New(); m_MaskImageNode->SetName("Mask"); auto pic3DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedBinMask.nrrd"); m_Mask = mitk::IOUtil::Load(pic3DCroppedBinMaskFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_Mask.IsNotNull()); m_MaskImageNode->SetData(m_Mask); m_DataStorage->Add(m_MaskImageNode); m_PFNode = mitk::DataNode::New(); m_PFNode->SetName("PF"); auto pic3DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedPF.pf"); m_PF = mitk::IOUtil::Load(pic3DCroppedPlanarFigureFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D planar figure", m_PF.IsNotNull()); m_PFNode->SetData(m_PF); m_DataStorage->Add(m_PFNode); int argc = 0; char** argv = nullptr; m_TestApp = new QCoreApplication(argc, argv); } void tearDown() override { delete m_TestApp; } bool CheckResultNode(const std::vector resultNodes, const mitk::DataNode* imageNode, const mitk::DataNode* roiNode, unsigned int histBin = 100, bool noZero = false) { for (auto& resultNode : resultNodes) { bool result = false; if (resultNode && resultNode->GetData() && imageNode && imageNode->GetData()) { auto imageRule = mitk::StatisticsToImageRelationRule::New(); result = !imageRule->GetRelationUIDs(resultNode, imageNode).empty(); if (roiNode) { auto maskRule = mitk::StatisticsToMaskRelationRule::New(); result = result && !maskRule->GetRelationUIDs(resultNode, roiNode).empty(); } auto prop = resultNode->GetData()->GetProperty(mitk::STATS_HISTOGRAM_BIN_PROPERTY_NAME.c_str()); auto binProp = dynamic_cast(prop.GetPointer()); result = result && binProp->GetValue() == histBin; prop = resultNode->GetData()->GetProperty(mitk::STATS_IGNORE_ZERO_VOXEL_PROPERTY_NAME.c_str()); auto zeroProp = dynamic_cast(prop.GetPointer()); result = result && zeroProp->GetValue() == noZero; } if (result) { //node was in the result set return true; } } return false; } void NullTest() { TestQmitkImageStatisticsDataGenerator generator(nullptr); generator.Generate(); CPPUNIT_ASSERT_EQUAL(0, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(0, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(generator.m_NewDataAvailable.empty()); generator.SetDataStorage(m_DataStorage); generator.Generate(); CPPUNIT_ASSERT_EQUAL(0, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(0, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(generator.m_NewDataAvailable.empty()); } void GetterSetterTest() { TestQmitkImageStatisticsDataGenerator generator(nullptr); CPPUNIT_ASSERT(nullptr == generator.GetDataStorage()); generator.SetDataStorage(m_DataStorage); CPPUNIT_ASSERT(m_DataStorage == generator.GetDataStorage()); TestQmitkImageStatisticsDataGenerator generator2(m_DataStorage); CPPUNIT_ASSERT(m_DataStorage == generator.GetDataStorage()); CPPUNIT_ASSERT_EQUAL(100u, generator.GetHistogramNBins()); CPPUNIT_ASSERT_EQUAL(false, generator.GetIgnoreZeroValueVoxel()); CPPUNIT_ASSERT_EQUAL(false, generator.GetAutoUpdate()); generator.SetHistogramNBins(3); CPPUNIT_ASSERT_EQUAL(3u, generator.GetHistogramNBins()); CPPUNIT_ASSERT_EQUAL(false, generator.GetIgnoreZeroValueVoxel()); CPPUNIT_ASSERT_EQUAL(false, generator.GetAutoUpdate()); generator.SetIgnoreZeroValueVoxel(true); CPPUNIT_ASSERT_EQUAL(3u, generator.GetHistogramNBins()); CPPUNIT_ASSERT_EQUAL(true, generator.GetIgnoreZeroValueVoxel()); CPPUNIT_ASSERT_EQUAL(false, generator.GetAutoUpdate()); generator.SetAutoUpdate(true); CPPUNIT_ASSERT_EQUAL(3u, generator.GetHistogramNBins()); CPPUNIT_ASSERT_EQUAL(true, generator.GetIgnoreZeroValueVoxel()); CPPUNIT_ASSERT_EQUAL(true, generator.GetAutoUpdate()); } void OneImageTest() { TestQmitkImageStatisticsDataGenerator generator(m_DataStorage); + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes { m_ImageNode1 }; - generator.SetImageNodes({ m_ImageNode1 }); + generator.SetImageNodes(imageNodes); generator.Generate(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size()); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable.front()->front() }, m_ImageNode1, nullptr)); CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate()); } void MultiImageTest() { TestQmitkImageStatisticsDataGenerator generator(m_DataStorage); + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes { m_ImageNode1, m_ImageNode2 }; - generator.SetImageNodes({ m_ImageNode1, m_ImageNode2 }); + generator.SetImageNodes(imageNodes); generator.Generate(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(2u == generator.m_NewDataAvailable.size()); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front() }, m_ImageNode1, nullptr)); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front() }, m_ImageNode2, nullptr)); CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate()); } void ImageAndROITest() { TestQmitkImageStatisticsDataGenerator generator(m_DataStorage); + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes { m_ImageNode1 }; + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes { m_MaskImageNode }; - generator.SetImageNodes({ m_ImageNode1 }); - generator.SetROINodes({ m_MaskImageNode }); + generator.SetImageNodes(imageNodes); + generator.SetROINodes(roiNodes); generator.Generate(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size()); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front() }, m_ImageNode1, m_MaskImageNode)); CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate()); - generator.SetROINodes({ m_PFNode }); + roiNodes = { m_PFNode }; + + generator.SetROINodes(roiNodes); generator.Generate(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(2, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(2 == generator.m_NewDataAvailable.size()); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[1]->front() }, m_ImageNode1, m_PFNode)); CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate()); } void ImageAndMultiROITest() { TestQmitkImageStatisticsDataGenerator generator(m_DataStorage); + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1 }; + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_PFNode, m_MaskImageNode, nullptr }; - generator.SetImageNodes({ m_ImageNode1 }); - generator.SetROINodes({ m_PFNode, m_MaskImageNode, nullptr }); + generator.SetImageNodes(imageNodes); + generator.SetROINodes(roiNodes); generator.Generate(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL(3, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(3 == generator.m_NewDataAvailable.size()); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front()}, m_ImageNode1, m_PFNode)); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front() }, m_ImageNode1, m_MaskImageNode)); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front() }, m_ImageNode1, nullptr)); CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate()); } void MultiMultiTest() { TestQmitkImageStatisticsDataGenerator generator(m_DataStorage); + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1, m_ImageNode2 }; + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_PFNode, m_MaskImageNode, nullptr }; - generator.SetImageNodes({ m_ImageNode1, m_ImageNode2 }); - generator.SetROINodes({ m_PFNode, m_MaskImageNode, nullptr }); + generator.SetImageNodes(imageNodes); + generator.SetROINodes(roiNodes); generator.Generate(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL(6, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(6 == generator.m_NewDataAvailable.size()); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(), generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode1, m_PFNode)); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(), generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode1, m_MaskImageNode)); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(), generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode1, nullptr)); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(), generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode2, m_PFNode)); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(), generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode2, m_MaskImageNode)); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front(), generator.m_NewDataAvailable[1]->front(), generator.m_NewDataAvailable[2]->front(), generator.m_NewDataAvailable[3]->front(), generator.m_NewDataAvailable[4]->front(), generator.m_NewDataAvailable[5]->front() }, m_ImageNode2, nullptr)); CPPUNIT_ASSERT_MESSAGE("Error: Rerun has generated new data.", generator.Generate()); } void InputChangedTest() { TestQmitkImageStatisticsDataGenerator generator(m_DataStorage); + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode2 }; - generator.SetImageNodes({ m_ImageNode2 }); + generator.SetImageNodes(imageNodes); m_TestApp->processEvents(); CPPUNIT_ASSERT_EQUAL(0, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(0, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(0 == generator.m_NewDataAvailable.size()); + imageNodes = { m_ImageNode1 }; + generator.SetAutoUpdate(true); - generator.SetImageNodes({ m_ImageNode1 }); + generator.SetImageNodes(imageNodes); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 1, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 1, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 1 == generator.m_NewDataAvailable.size()); - generator.SetImageNodes({ m_ImageNode1 }); + generator.SetImageNodes(imageNodes); m_TestApp->processEvents(); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 1, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 1, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 1 == generator.m_NewDataAvailable.size()); + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_MaskImageNode }; + generator.SetAutoUpdate(true); - generator.SetROINodes({ m_MaskImageNode }); + generator.SetROINodes(roiNodes); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 2, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 2, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 2 == generator.m_NewDataAvailable.size()); } void SettingsChangedTest() { TestQmitkImageStatisticsDataGenerator generator(m_DataStorage); + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1 }; - generator.SetImageNodes({ m_ImageNode1 }); + generator.SetImageNodes(imageNodes); generator.Generate(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size()); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[0]->front() }, m_ImageNode1, nullptr)); generator.SetHistogramNBins(50); generator.Generate(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(2, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(2 == generator.m_NewDataAvailable.size()); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[1]->front() }, m_ImageNode1, nullptr, 50)); generator.SetIgnoreZeroValueVoxel(true); generator.Generate(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL(3, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(3, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(3 == generator.m_NewDataAvailable.size()); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[2]->front() }, m_ImageNode1, nullptr, 50, true)); //now check auto update feature generator.SetAutoUpdate(true); generator.SetHistogramNBins(5); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 4, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 4, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 4 == generator.m_NewDataAvailable.size()); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[3]->front() }, m_ImageNode1, nullptr, 5, true)); generator.SetHistogramNBins(5); m_TestApp->processEvents(); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 4, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 4, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 4 == generator.m_NewDataAvailable.size()); generator.SetIgnoreZeroValueVoxel(false); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 5, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 5, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 5 == generator.m_NewDataAvailable.size()); CPPUNIT_ASSERT(CheckResultNode({ generator.m_NewDataAvailable[4]->front() }, m_ImageNode1, nullptr, 5, false)); generator.SetIgnoreZeroValueVoxel(false); m_TestApp->processEvents(); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 5, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 5, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but input does not realy changed.", 5 == generator.m_NewDataAvailable.size()); } void DataStorageModificationTest() { TestQmitkImageStatisticsDataGenerator generator(m_DataStorage); + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType imageNodes{ m_ImageNode1 }; + QmitkImageAndRoiDataGeneratorBase::ConstNodeVectorType roiNodes{ m_PFNode }; - generator.SetImageNodes({ m_ImageNode1 }); - generator.SetROINodes({ m_PFNode }); + generator.SetImageNodes(imageNodes); + generator.SetROINodes(roiNodes); generator.Generate(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size()); m_PF->Modified(); m_PFNode->Modified(); m_TestApp->processEvents(); CPPUNIT_ASSERT_EQUAL(1, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(1, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(1 == generator.m_NewDataAvailable.size()); generator.Generate(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL(2, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL(2, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT(generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT(2 == generator.m_NewDataAvailable.size()); //now check auto update feature generator.SetAutoUpdate(true); m_PF->Modified(); m_PFNode->Modified(); m_TestApp->exec(); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 3, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update seemed not to work.", 3, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT_MESSAGE("Error: Auto update seemed not to work.", 3 == generator.m_NewDataAvailable.size()); m_DataStorage->Add(mitk::DataNode::New()); m_TestApp->processEvents(); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3 == generator.m_NewDataAvailable.size()); m_Image2->Modified(); m_ImageNode2->Modified(); m_TestApp->processEvents(); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_DataGenerationStartedEmited); CPPUNIT_ASSERT_EQUAL_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3, generator.m_GenerationFinishedEmited); CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", generator.m_JobErrorEmited_error.empty()); CPPUNIT_ASSERT_MESSAGE("Error: Auto update was triggerd, but only irrelevant node was added.", 3 == generator.m_NewDataAvailable.size()); } }; MITK_TEST_SUITE_REGISTRATION(QmitkImageStatisticsDataGenerator) diff --git a/Modules/LegacyIO/mitkImageWriter.cpp b/Modules/LegacyIO/mitkImageWriter.cpp index 3405fbb559..862c5a12bf 100644 --- a/Modules/LegacyIO/mitkImageWriter.cpp +++ b/Modules/LegacyIO/mitkImageWriter.cpp @@ -1,458 +1,458 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkImageWriter.h" #include "mitkImage.h" #include "mitkImageAccessByItk.h" #include "mitkImageReadAccessor.h" #include "mitkImageTimeSelector.h" #include "mitkItkPictureWrite.h" #include #include #include mitk::ImageWriter::ImageWriter() : m_UseCompression(true) { this->SetNumberOfRequiredInputs(1); m_MimeType = ""; SetDefaultExtension(); } mitk::ImageWriter::~ImageWriter() { } void mitk::ImageWriter::SetFileName(const char *fileName) { if (fileName && (fileName == this->m_FileName)) { return; } if (fileName) { this->m_FileName = fileName; this->m_FileNameWithoutExtension = this->m_FileName; this->m_Extension.clear(); std::size_t pos = this->m_FileName.find_last_of("/\\"); if (pos != std::string::npos) { std::size_t ppos = this->m_FileName.find_first_of('.', pos); if (ppos != std::string::npos) { this->m_FileNameWithoutExtension = this->m_FileName.substr(0, ppos); this->m_Extension = this->m_FileName.substr(ppos); } } } else { this->m_FileName.clear(); this->m_FileNameWithoutExtension.clear(); this->m_Extension.clear(); } this->Modified(); } void mitk::ImageWriter::SetFileName(const std::string &fileName) { this->SetFileName(fileName.c_str()); } void mitk::ImageWriter::SetExtension(const char *extension) { if (extension && (extension == this->m_Extension)) { return; } if (extension) { this->m_Extension = extension; this->m_FileName = this->m_FileNameWithoutExtension + this->m_Extension; } else { this->m_Extension.clear(); this->m_FileName = this->m_FileNameWithoutExtension; } this->Modified(); } void mitk::ImageWriter::SetExtension(const std::string &extension) { this->SetFileName(extension.c_str()); } void mitk::ImageWriter::SetDefaultExtension() { this->m_Extension = ".mhd"; this->m_FileName = this->m_FileNameWithoutExtension + this->m_Extension; this->Modified(); } #include #include #include static void writeVti(const char *filename, mitk::Image *image, int t = 0) { vtkXMLImageDataWriter *vtkwriter = vtkXMLImageDataWriter::New(); vtkwriter->SetFileName(filename); vtkwriter->SetInputData(image->GetVtkImageData(t)); vtkwriter->Write(); vtkwriter->Delete(); } #include void mitk::ImageWriter::WriteByITK(mitk::Image *image, const std::string &fileName) { MITK_INFO << "Writing image: " << fileName << std::endl; // Pictures and picture series like .png are written via a different mechanism then volume images. // So, they are still multiplexed and thus not support vector images. if (fileName.find(".png") != std::string::npos || fileName.find(".tif") != std::string::npos || fileName.find(".jpg") != std::string::npos || fileName.find(".bmp") != std::string::npos) { try { // switch processing of single/multi-component images if (image->GetPixelType(0).GetNumberOfComponents() == 1) { AccessByItk_1(image, _mitkItkPictureWrite, fileName); } else { AccessFixedPixelTypeByItk_1(image, _mitkItkPictureWriteComposite, MITK_ACCESSBYITK_PIXEL_TYPES_SEQ MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES_SEQ, fileName); } } catch (itk::ExceptionObject &e) { std::cerr << "Caught " << e.what() << std::endl; } catch (std::exception &e) { std::cerr << "Caught std::exception " << e.what() << std::endl; } return; } // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. unsigned int dimension = image->GetDimension(); unsigned int *dimensions = image->GetDimensions(); mitk::PixelType pixelType = image->GetPixelType(); mitk::Vector3D mitkSpacing = image->GetGeometry()->GetSpacing(); mitk::Point3D mitkOrigin = image->GetGeometry()->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 an 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 an valid value here itk::ImageIOBase::Pointer imageIO = - itk::ImageIOFactory::CreateImageIO(fileName.c_str(), itk::ImageIOFactory::WriteMode); + itk::ImageIOFactory::CreateImageIO(fileName.c_str(), itk::IOFileModeEnum::WriteMode); if (imageIO.IsNull()) { itkExceptionMacro(<< "Error: Could not create itkImageIO via factory for file " << fileName); } // Set the necessary information for imageIO imageIO->SetNumberOfDimensions(dimension); imageIO->SetPixelType(pixelType.GetPixelType()); - imageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? - static_cast(pixelType.GetComponentType()) : - itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); + imageIO->SetComponentType(static_cast(pixelType.GetComponentType()) < PixelComponentUserType + ? pixelType.GetComponentType() + : itk::IOComponentEnum::UNKNOWNCOMPONENTTYPE); imageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { imageIO->SetDimensions(i, dimensions[i]); imageIO->SetSpacing(i, spacing4D[i]); imageIO->SetOrigin(i, origin4D[i]); - mitk::Vector3D mitkDirection; + mitk::Vector3D mitkDirection(0.0); mitkDirection.SetVnlVector( - image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); + image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref()); 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]; } imageIO->SetDirection(i, axisDirection); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available imageIO->SetUseCompression(m_UseCompression); imageIO->SetIORegion(ioRegion); imageIO->SetFileName(fileName); ImageReadAccessor imageAccess(image); imageIO->Write(imageAccess.GetData()); } void mitk::ImageWriter::GenerateData() { mitk::LocaleSwitch localeSwitch("C"); if (m_FileName == "") { itkWarningMacro(<< "Sorry, filename has not been set!"); return; } FILE *tempFile = fopen(m_FileName.c_str(), "w"); if (tempFile == nullptr) { itkExceptionMacro(<< "File location not writeable"); return; } fclose(tempFile); remove(m_FileName.c_str()); // Creating clone of input image, since i might change the geometry mitk::Image::Pointer input = this->GetInput()->Clone(); // Check if geometry information will be lost if (input->GetDimension() == 2) { if (!input->GetGeometry()->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 = input->GetGeometry()->GetSpacing(); mitk::Point3D origin = input->GetGeometry()->GetOrigin(); input->GetGeometry()->SetIndexToWorldTransform(affTrans); input->GetGeometry()->SetSpacing(spacing); input->GetGeometry()->SetOrigin(origin); } } bool vti = (m_Extension.find(".vti") != std::string::npos); // If the extension is NOT .nrrd and NOT .nii and NOT .nii.gz the following block is entered if (m_Extension.find(".nrrd") == std::string::npos && m_Extension.find(".nii") == std::string::npos && m_Extension.find(".nii.gz") == std::string::npos) { if (input->GetDimension() > 3) { int t, timesteps; timesteps = input->GetDimension(3); ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(input); mitk::Image::Pointer image = timeSelector->GetOutput(); for (t = 0; t < timesteps; ++t) { std::ostringstream filename; timeSelector->SetTimeNr(t); timeSelector->Update(); if (input->GetTimeGeometry()->IsValidTimeStep(t)) { const mitk::TimeBounds timebounds = input->GetTimeGeometry()->GetTimeBounds(t); filename << m_FileNameWithoutExtension << "_S" << std::setprecision(0) << timebounds[0] << "_E" << std::setprecision(0) << timebounds[1] << "_T" << t << m_Extension; } else { itkWarningMacro(<< "Error on write: TimeGeometry invalid of image " << filename.str() << "."); filename << m_FileNameWithoutExtension << "_T" << t << m_Extension; } if (vti) { writeVti(filename.str().c_str(), input, t); } else { WriteByITK(image, filename.str()); } } } else if (vti) { writeVti(m_FileName.c_str(), input); } else { WriteByITK(input, m_FileName); } } else { if (m_Extension.find(".nrrd") != std::string::npos || m_Extension.find(".nii") != std::string::npos || m_Extension.find(".nii.gz") != std::string::npos) { WriteByITK(input, this->m_FileName); } else { itkExceptionMacro(<< "File type not writeable"); } } m_MimeType = "application/MITK.Pic"; } bool mitk::ImageWriter::CanWriteDataType(DataNode *input) { if (input) { return this->CanWriteBaseDataType(input->GetData()); } return false; } void mitk::ImageWriter::SetInput(DataNode *input) { if (input && CanWriteDataType(input)) this->ProcessObject::SetNthInput(0, dynamic_cast(input->GetData())); } std::string mitk::ImageWriter::GetWritenMIMEType() { return m_MimeType; } std::vector mitk::ImageWriter::GetPossibleFileExtensions() { std::vector possibleFileExtensions; possibleFileExtensions.push_back(".bmp"); possibleFileExtensions.push_back(".dcm"); possibleFileExtensions.push_back(".DCM"); possibleFileExtensions.push_back(".dicom"); possibleFileExtensions.push_back(".DICOM"); possibleFileExtensions.push_back(".gipl"); possibleFileExtensions.push_back(".gipl.gz"); possibleFileExtensions.push_back(".mha"); possibleFileExtensions.push_back(".nii"); possibleFileExtensions.push_back(".nii.gz"); possibleFileExtensions.push_back(".nrrd"); possibleFileExtensions.push_back(".nhdr"); possibleFileExtensions.push_back(".png"); possibleFileExtensions.push_back(".PNG"); possibleFileExtensions.push_back(".spr"); possibleFileExtensions.push_back(".mhd"); possibleFileExtensions.push_back(".vtk"); possibleFileExtensions.push_back(".vti"); possibleFileExtensions.push_back(".hdr"); possibleFileExtensions.push_back(".img"); possibleFileExtensions.push_back(".img.gz"); possibleFileExtensions.push_back(".png"); possibleFileExtensions.push_back(".tif"); possibleFileExtensions.push_back(".jpg"); return possibleFileExtensions; } std::string mitk::ImageWriter::GetSupportedBaseData() const { return Image::GetStaticNameOfClass(); } std::string mitk::ImageWriter::GetFileExtension() { return m_Extension; } void mitk::ImageWriter::SetInput(mitk::Image *image) { this->ProcessObject::SetNthInput(0, image); } const mitk::Image *mitk::ImageWriter::GetInput() { if (this->GetNumberOfInputs() < 1) { return nullptr; } else { return static_cast(this->ProcessObject::GetInput(0)); } } const char *mitk::ImageWriter::GetDefaultFilename() { return "Image.nrrd"; } const char *mitk::ImageWriter::GetFileDialogPattern() { return "Nearly Raw Raster Data (*.nrrd);;" "NIfTI format (*.nii *.nii.gz);;" "VTK Image Data Files (*.vti);;" "NRRD with detached header (*.nhdr);;" "Analyze Format (*.hdr);;" "MetaImage (*.mhd);;" "Sets of 2D slices (*.png *.tiff *.jpg *.jpeg *.bmp);;" "DICOM (*.dcm *.DCM *.dicom *.DICOM);;" "UMDS GIPL Format Files (*.gipl *.gipl.gz)"; } const char *mitk::ImageWriter::GetDefaultExtension() { return ".nrrd"; } bool mitk::ImageWriter::CanWriteBaseDataType(BaseData::Pointer data) { return dynamic_cast(data.GetPointer()); } void mitk::ImageWriter::DoWrite(BaseData::Pointer data) { if (this->CanWriteBaseDataType(data)) { this->SetInput(dynamic_cast(data.GetPointer())); this->Update(); } } void mitk::ImageWriter::SetUseCompression(bool useCompression) { m_UseCompression = useCompression; } diff --git a/Modules/LegacyIO/mitkItkImageFileReader.cpp b/Modules/LegacyIO/mitkItkImageFileReader.cpp index ef09bea936..7cbd6e1fd0 100644 --- a/Modules/LegacyIO/mitkItkImageFileReader.cpp +++ b/Modules/LegacyIO/mitkItkImageFileReader.cpp @@ -1,214 +1,214 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkItkImageFileReader.h" #include "mitkConfig.h" #include "mitkException.h" #include #include #include #include #include #include //#include #include #include #include #include //#include //#include //#include //#include //#include //#include void mitk::ItkImageFileReader::GenerateData() { mitk::LocaleSwitch localeSwitch("C"); mitk::Image::Pointer image = this->GetOutput(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; MITK_INFO("mitkItkImageFileReader") << "loading " << m_FileName << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (m_FileName == "") { mitkThrow() << "Empty filename in mitk::ItkImageFileReader "; return; } itk::ImageIOBase::Pointer imageIO = - itk::ImageIOFactory::CreateImageIO(m_FileName.c_str(), itk::ImageIOFactory::ReadMode); + itk::ImageIOFactory::CreateImageIO(m_FileName.c_str(), itk::IOFileModeEnum::ReadMode); if (imageIO.IsNull()) { // itkWarningMacro( << "File Type not supported!" ); mitkThrow() << "Could not create itk::ImageIOBase object for filename " << m_FileName; return; } // Got to allocate space for the image. Determine the characteristics of // the image. imageIO->SetFileName(m_FileName.c_str()); imageIO->ReadImageInformation(); unsigned int ndim = imageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { itkWarningMacro(<< "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] = imageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = imageIO->GetDimensions(i); spacing[i] = imageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = imageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO("mitkItkImageFileReader") << "ioRegion: " << ioRegion << std::endl; imageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[imageIO->GetImageSizeInBytes()]; imageIO->Read(buffer); image->Initialize(MakePixelType(imageIO), 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] = imageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = static_cast(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("mitkItkImageFileReader") << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO("mitkItkImageFileReader") << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO("mitkItkImageFileReader") << "number of image components: " << image->GetPixelType().GetNumberOfComponents() << std::endl; // mitk::DataNode::Pointer node = this->GetOutput(); // node->SetData( image ); // add level-window property // if ( image->GetPixelType().GetNumberOfComponents() == 1 ) //{ // SetDefaultImageProperties( node ); //} MITK_INFO("mitkItkImageFileReader") << "...finished!" << std::endl; } bool mitk::ItkImageFileReader::CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern) { // First check the extension if (filename == "") return false; // check if image is serie if (filePattern != "" && filePrefix != "") return false; itk::ImageIOBase::Pointer imageIO = - itk::ImageIOFactory::CreateImageIO(filename.c_str(), itk::ImageIOFactory::ReadMode); + itk::ImageIOFactory::CreateImageIO(filename.c_str(), itk::IOFileModeEnum::ReadMode); if (imageIO.IsNull()) return false; try { imageIO->SetFileName(filename.c_str()); imageIO->ReadImageInformation(); itk::MetaDataDictionary imgMetaDictionary = imageIO->GetMetaDataDictionary(); std::vector imgMetaKeys = imgMetaDictionary.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(imgMetaDictionary, *itKey, metaString); if (itKey->find("modality") != std::string::npos) { if (metaString.find("DWMRI") != std::string::npos) { return false; // DiffusionImageReader should handle this } } } } catch (...) { MITK_INFO("mitkItkImageFileReader") << "Could not read ImageInformation "; } return true; } mitk::ItkImageFileReader::ItkImageFileReader() : m_FileName(""), m_FilePrefix(""), m_FilePattern("") { } mitk::ItkImageFileReader::~ItkImageFileReader() { } diff --git a/Modules/MatchPointRegistration/include/itkStitchImageFilter.h b/Modules/MatchPointRegistration/include/itkStitchImageFilter.h index 5d5edce5d0..c72836aeca 100644 --- a/Modules/MatchPointRegistration/include/itkStitchImageFilter.h +++ b/Modules/MatchPointRegistration/include/itkStitchImageFilter.h @@ -1,330 +1,330 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 itkStitchImageFilter_h #define itkStitchImageFilter_h #include "itkFixedArray.h" #include "itkTransform.h" #include "itkImageRegionIterator.h" #include "itkImageToImageFilter.h" #include "itkLinearInterpolateImageFunction.h" #include "itkSize.h" #include "itkDefaultConvertPixelTraits.h" #include "itkDataObjectDecorator.h" namespace itk { enum class StitchStrategy { Mean = 0, //use the mean value of all inputs that can provide a pixel vaule BorderDistance = 1 //use the value that is largest minimal distance to its image borders (use e.g. if vaules tend to be not reliable at borders) }; std::ostream& operator<< (std::ostream& os, const itk::StitchStrategy& strategy) { if (itk::StitchStrategy::Mean == strategy) os << "Mean"; else if (itk::StitchStrategy::BorderDistance == strategy) os << "BorderDistance"; else os << "unkown"; return os; }; /** \class StitchImageFilter * \brief ITK filter that resamples/stitches multiple images into a given reference geometry. * * StitchImageFilter is similar to itk's ResampleImageFilter, but in difference to the last * mentioned StitchImageFilter is able to resample multiple input images at once (with a transform * for each input image). If multiple input images cover the output region the behavior depends on * the StitchStragy: * - Mean: a weighted sum of all voxels mapped input pixel values will be calculated. * - BorderDistance: the voxels will be choosen that have the largest minimal distance to its own image borders. * * All other behaviors are similar to itk::ResampleImageFilter. See the filter's description for * more details. */ template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType = double, typename TTransformPrecisionType = TInterpolatorPrecisionType> class StitchImageFilter : public ImageToImageFilter< TInputImage, TOutputImage > { public: /** Standard class typedefs. */ typedef StitchImageFilter Self; typedef ImageToImageFilter< TInputImage, TOutputImage > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef TInputImage InputImageType; typedef TOutputImage OutputImageType; typedef typename InputImageType::Pointer InputImagePointer; typedef typename InputImageType::ConstPointer InputImageConstPointer; typedef typename OutputImageType::Pointer OutputImagePointer; typedef typename InputImageType::RegionType InputImageRegionType; /** Method for creation through the object factory. */ itkNewMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(StitchImageFilter, ImageToImageFilter); /** Number of dimensions. */ itkStaticConstMacro(ImageDimension, unsigned int, TOutputImage::ImageDimension); itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); /** base type for images of the current ImageDimension */ typedef ImageBase< itkGetStaticConstMacro(ImageDimension) > ImageBaseType; /** * Transform typedef. */ typedef Transform< TTransformPrecisionType, itkGetStaticConstMacro(ImageDimension), itkGetStaticConstMacro(ImageDimension) > TransformType; typedef typename TransformType::ConstPointer TransformPointerType; typedef DataObjectDecorator DecoratedTransformType; typedef typename DecoratedTransformType::Pointer DecoratedTransformPointer; /** Interpolator typedef. */ typedef InterpolateImageFunction< InputImageType, TInterpolatorPrecisionType > InterpolatorType; typedef typename InterpolatorType::Pointer InterpolatorPointerType; typedef typename InterpolatorType::OutputType InterpolatorOutputType; typedef DefaultConvertPixelTraits< InterpolatorOutputType > InterpolatorConvertType; typedef typename InterpolatorConvertType::ComponentType ComponentType; typedef LinearInterpolateImageFunction< InputImageType, TInterpolatorPrecisionType > LinearInterpolatorType; typedef typename LinearInterpolatorType::Pointer LinearInterpolatorPointerType; /** Image size typedef. */ typedef Size< itkGetStaticConstMacro(ImageDimension) > SizeType; /** Image index typedef. */ typedef typename TOutputImage::IndexType IndexType; /** Image point typedef. */ typedef typename InterpolatorType::PointType PointType; //typedef typename TOutputImage::PointType PointType; /** Image pixel value typedef. */ typedef typename TOutputImage::PixelType PixelType; typedef typename TInputImage::PixelType InputPixelType; typedef DefaultConvertPixelTraits PixelConvertType; typedef typename PixelConvertType::ComponentType PixelComponentType; /** Input pixel continuous index typdef */ typedef ContinuousIndex< TTransformPrecisionType, ImageDimension > ContinuousInputIndexType; /** Typedef to describe the output image region type. */ typedef typename TOutputImage::RegionType OutputImageRegionType; /** Image spacing,origin and direction typedef */ typedef typename TOutputImage::SpacingType SpacingType; typedef typename TOutputImage::PointType OriginPointType; typedef typename TOutputImage::DirectionType DirectionType; using Superclass::GetInput; /** Typedef the reference image type to be the ImageBase of the OutputImageType */ typedef ImageBase ReferenceImageBaseType; using Superclass::SetInput; void SetInput(const InputImageType* image) override; void SetInput(unsigned int index, const InputImageType* image) override; /** Convinience methods that allows setting of input image and its transform in one call.*/ virtual void SetInput(unsigned int index, const InputImageType* image, const TransformType* transform); virtual void SetInput(unsigned int index, const InputImageType* image, const TransformType* transform, InterpolatorType* interpolator); const TransformType* GetTransform(unsigned int index) const; const InterpolatorType* GetInterpolator(unsigned int index) const; /** Get/Set the size of the output image. */ itkSetMacro(Size, SizeType); itkGetConstReferenceMacro(Size, SizeType); /** Get/Set the pixel value when a transformed pixel is outside of the * image. The default default pixel value is 0. */ itkSetMacro(DefaultPixelValue, PixelType); itkGetConstReferenceMacro(DefaultPixelValue, PixelType); /** Set the output image spacing. */ itkSetMacro(OutputSpacing, SpacingType); virtual void SetOutputSpacing(const double *values); /** Get the output image spacing. */ itkGetConstReferenceMacro(OutputSpacing, SpacingType); /** Set the output image origin. */ itkSetMacro(OutputOrigin, OriginPointType); virtual void SetOutputOrigin(const double *values); /** Get the output image origin. */ itkGetConstReferenceMacro(OutputOrigin, OriginPointType); /** Set the output direciton cosine matrix. */ itkSetMacro(OutputDirection, DirectionType); itkGetConstReferenceMacro(OutputDirection, DirectionType); /** Helper method to set the output parameters based on this image. */ void SetOutputParametersFromImage(const ImageBaseType *image); /** Set the start index of the output largest possible region. * The default is an index of all zeros. */ itkSetMacro(OutputStartIndex, IndexType); /** Get the start index of the output largest possible region. */ itkGetConstReferenceMacro(OutputStartIndex, IndexType); /** Set a reference image to use to define the output information. * By default, output information is specificed through the * SetOutputSpacing, Origin, and Direction methods. Alternatively, * this method can be used to specify an image from which to * copy the information. UseReferenceImageOn must be set to utilize the * reference image. */ itkSetInputMacro(ReferenceImage, ReferenceImageBaseType); /** Get the reference image that is defining the output information. */ itkGetInputMacro(ReferenceImage, ReferenceImageBaseType); /** Turn on/off whether a specified reference image should be used to define * the output information. */ itkSetMacro(UseReferenceImage, bool); itkBooleanMacro(UseReferenceImage); itkGetConstMacro(UseReferenceImage, bool); itkSetMacro(StitchStrategy, StitchStrategy); itkGetConstMacro(StitchStrategy, StitchStrategy); /** StitchImageFilter produces an image which is a different size * than its input. As such, it needs to provide an implementation * for GenerateOutputInformation() in order to inform the pipeline * execution model. The original documentation of this method is * below. \sa ProcessObject::GenerateOutputInformaton() */ virtual void GenerateOutputInformation() ITK_OVERRIDE; /** StitchImageFilter needs a different input requested region than * the output requested region. As such, StitchImageFilter needs * to provide an implementation for GenerateInputRequestedRegion() * in order to inform the pipeline execution model. * \sa ProcessObject::GenerateInputRequestedRegion() */ virtual void GenerateInputRequestedRegion() ITK_OVERRIDE; /** Set up state of filter before multi-threading. * InterpolatorType::SetInputImage is not thread-safe and hence * has to be set up before ThreadedGenerateData */ virtual void BeforeThreadedGenerateData() ITK_OVERRIDE; /** Set the state of the filter after multi-threading. */ virtual void AfterThreadedGenerateData() ITK_OVERRIDE; /** Compute the Modified Time based on the changed components. */ ModifiedTimeType GetMTime(void) const ITK_OVERRIDE; #ifdef ITK_USE_CONCEPT_CHECKING // Begin concept checking itkConceptMacro( OutputHasNumericTraitsCheck, ( Concept::HasNumericTraits< PixelComponentType > ) ); // End concept checking #endif protected: StitchImageFilter(); ~StitchImageFilter() ITK_OVERRIDE {} void PrintSelf(std::ostream & os, Indent indent) const ITK_OVERRIDE; /** Override VeriyInputInformation() since this filter's inputs do * not need to occoupy the same physical space. * * \sa ProcessObject::VerifyInputInformation */ - virtual void VerifyInputInformation() ITK_OVERRIDE { } + virtual void VerifyInputInformation() const ITK_OVERRIDE { } /** StitchImageFilter can be implemented as a multithreaded filter. * Therefore, this implementation provides a ThreadedGenerateData() * routine which is called for each processing thread. The output * image data is allocated automatically by the superclass prior * to calling ThreadedGenerateData(). * ThreadedGenerateData can only write to the portion of the output image * specified by the parameter "outputRegionForThread" * \sa ImageToImageFilter::ThreadedGenerateData(), * ImageToImageFilter::GenerateData() */ virtual void ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) ITK_OVERRIDE; /** Cast pixel from interpolator output to PixelType. */ virtual PixelType CastPixelWithBoundsChecking( const InterpolatorOutputType value, const ComponentType minComponent, const ComponentType maxComponent) const; void SetTransform(unsigned int index, const TransformType* transform); /** Helper that ensures that a transform is specified for every input image. If a input image has no specified transforms, an identity transform will be created and set as default.*/ void EnsureTransforms(); /** Helper that ensures that an interpolator is specified for every input image. If a input image has no specified interpolator, a linear interpolator will be created and set as default.*/ void EnsureInterpolators(); static std::string GetTransformInputName(unsigned int index); private: ITK_DISALLOW_COPY_AND_ASSIGN(StitchImageFilter); typedef std::vector InputImageVectorType; typedef std::map TransformMapType; typedef std::map InterpolatorMapType; InputImageVectorType GetInputs(); TransformMapType GetTransforms(); InterpolatorMapType m_Interpolators; // Image function for // interpolation PixelType m_DefaultPixelValue; // default pixel value // if the point is // outside the image SizeType m_Size; // Size of the output image SpacingType m_OutputSpacing; // output image spacing OriginPointType m_OutputOrigin; // output image origin DirectionType m_OutputDirection; // output image direction cosines IndexType m_OutputStartIndex; // output image start index bool m_UseReferenceImage; StitchStrategy m_StitchStrategy; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkStitchImageFilter.tpp" #endif #endif diff --git a/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp b/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp index 5e8e7529c9..2b535e174f 100644 --- a/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp +++ b/Modules/MatchPointRegistration/include/itkStitchImageFilter.tpp @@ -1,639 +1,639 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 itkStitchImageFilter_hxx #define itkStitchImageFilter_hxx #include "itkStitchImageFilter.h" #include "itkObjectFactory.h" #include "itkIdentityTransform.h" #include "itkProgressReporter.h" #include "itkImageRegionIteratorWithIndex.h" #include "itkImageScanlineIterator.h" #include "itkSpecialCoordinatesImage.h" #include "itkDefaultConvertPixelTraits.h" #include "itkSimpleDataObjectDecorator.h" #include namespace itk { template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::StitchImageFilter() : m_OutputSpacing( 1.0 ), m_OutputOrigin( 0.0 ), m_UseReferenceImage( false ), m_StitchStrategy(StitchStrategy::Mean) { + this->DynamicMultiThreadingOff(); m_Size.Fill( 0 ); m_OutputStartIndex.Fill( 0 ); m_OutputDirection.SetIdentity(); // Pipeline input configuration // implicit input index set: // #1 "ReferenceImage" optional Self::AddOptionalInputName("ReferenceImage"); m_DefaultPixelValue = NumericTraits::ZeroValue( m_DefaultPixelValue ); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetInput(const InputImageType* image) { this->SetInput(0, image, itk::IdentityTransform< TTransformPrecisionType, ImageDimension>::New().GetPointer(), LinearInterpolatorType::New().GetPointer()); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetInput(unsigned int index, const InputImageType* image) { this->SetInput(index, image, itk::IdentityTransform< TTransformPrecisionType, ImageDimension>::New().GetPointer(), LinearInterpolatorType::New().GetPointer()); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetInput(unsigned int index, const InputImageType* image, const TransformType* transform) { this->SetInput(index, image, transform, LinearInterpolatorType::New().GetPointer()); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetInput(unsigned int index, const InputImageType* image, const TransformType* transform, InterpolatorType* interpolator) { Superclass::SetInput(index, image); m_Interpolators[image] = interpolator; this->SetTransform(index, transform); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetTransform(unsigned int index, const TransformType* transform) { const auto transformName = this->GetTransformInputName(index); typedef SimpleDataObjectDecorator< TransformPointerType > DecoratorType; const DecoratorType* oldInput = itkDynamicCastInDebugMode< const DecoratorType* >(this->ProcessObject::GetInput(transformName)); if (!oldInput || oldInput->Get() != transform) { typename DecoratorType::Pointer newInput = DecoratorType::New(); // Process object is not const-correct so the const_cast is required here newInput->Set(const_cast(transform)); this->ProcessObject::SetInput(transformName, newInput); } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > const typename StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >::TransformType* StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GetTransform(unsigned int index) const { typedef SimpleDataObjectDecorator< TransformPointerType > DecoratorType; const DecoratorType* input = itkDynamicCastInDebugMode< const DecoratorType* >(this->ProcessObject::GetInput(this->GetTransformInputName(index))); if (nullptr != input) { return input->Get(); } return nullptr; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > const typename StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType >::InterpolatorType* StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GetInterpolator(unsigned int index) const { auto input = this->GetInput(index); if (m_Interpolators.find(input) != std::end(m_Interpolators)) { return m_Interpolators[input]; } return nullptr; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetOutputSpacing(const double *spacing) { SpacingType s; for(unsigned int i = 0; i < TOutputImage::ImageDimension; ++i) { s[i] = static_cast< typename SpacingType::ValueType >(spacing[i]); } this->SetOutputSpacing(s); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetOutputOrigin(const double *origin) { OriginPointType p(origin); this->SetOutputOrigin(p); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::SetOutputParametersFromImage(const ImageBaseType *image) { this->SetOutputOrigin ( image->GetOrigin() ); this->SetOutputSpacing ( image->GetSpacing() ); this->SetOutputDirection ( image->GetDirection() ); this->SetOutputStartIndex ( image->GetLargestPossibleRegion().GetIndex() ); this->SetSize ( image->GetLargestPossibleRegion().GetSize() ); } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::BeforeThreadedGenerateData() { this->EnsureInterpolators(); this->EnsureTransforms(); for (const auto& interpolator : m_Interpolators) { interpolator.second->SetInputImage(interpolator.first); } unsigned int nComponents = DefaultConvertPixelTraits::GetNumberOfComponents( m_DefaultPixelValue ); if (nComponents == 0) { PixelComponentType zeroComponent = NumericTraits::ZeroValue(); nComponents = this->GetInput()->GetNumberOfComponentsPerPixel(); NumericTraits::SetLength(m_DefaultPixelValue, nComponents ); for (unsigned int n=0; n void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::AfterThreadedGenerateData() { // Disconnect input image from the interpolator for (auto& interpolator : m_Interpolators) { interpolator.second->SetInputImage(ITK_NULLPTR); } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) { if( outputRegionForThread.GetNumberOfPixels() == 0 ) { return; } // Get the output pointers OutputImageType* outputPtr = this->GetOutput(); // Get this input pointers InputImageVectorType inputs = this->GetInputs(); TransformMapType transforms = this->GetTransforms(); std::map lowerIndices; std::map upperIndices; for (const auto& input : inputs) { const auto largestRegion = input->GetLargestPossibleRegion(); lowerIndices[input] = largestRegion.GetIndex(); upperIndices[input] = largestRegion.GetUpperIndex(); } // Create an iterator that will walk the output region for this thread. typedef ImageRegionIteratorWithIndex< OutputImageType > OutputIterator; OutputIterator outIt(outputPtr, outputRegionForThread); // Define a few indices that will be used to translate from an input pixel // to an output pixel PointType outputPoint; // Coordinates of current output pixel PointType inputPoint; // Coordinates of current input pixel ContinuousInputIndexType inputIndex; // Support for progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels()); // Min/max values of the output pixel type AND these values // represented as the output type of the interpolator const PixelComponentType minValue = NumericTraits< PixelComponentType >::NonpositiveMin(); const PixelComponentType maxValue = NumericTraits< PixelComponentType >::max(); typedef typename InterpolatorType::OutputType OutputType; const ComponentType minOutputValue = static_cast(minValue); const ComponentType maxOutputValue = static_cast(maxValue); // Walk the output region outIt.GoToBegin(); while (!outIt.IsAtEnd()) { // Determine the index of the current output pixel outputPtr->TransformIndexToPhysicalPoint(outIt.GetIndex(), outputPoint); std::vector pixvals; std::vector pixDistance; for (const auto& input : inputs) { // Compute corresponding input pixel position inputPoint = transforms[input]->TransformPoint(outputPoint); const bool isInsideInput = input->TransformPhysicalPointToContinuousIndex(inputPoint, inputIndex); // Evaluate input at right position and copy to the output if (m_Interpolators[input]->IsInsideBuffer(inputIndex) && isInsideInput) { OutputType value = m_Interpolators[input]->EvaluateAtContinuousIndex(inputIndex); pixvals.emplace_back(this->CastPixelWithBoundsChecking(value, minOutputValue, maxOutputValue)); - ContinuousInputIndexType indexDistance; const auto spacing = input->GetSpacing(); double minBorderDistance = std::numeric_limits::max(); for (unsigned int i = 0; i < ImageDimension; ++i) { minBorderDistance = std::min(minBorderDistance, std::min(std::abs(lowerIndices[input][i] - inputIndex[i]) * spacing[i], std::abs(upperIndices[input][i] - inputIndex[i]) * spacing[i])); } pixDistance.emplace_back(minBorderDistance); } } if (!pixvals.empty()) { //at least one input provided a value if (StitchStrategy::Mean == m_StitchStrategy) { double sum = std::accumulate(pixvals.begin(), pixvals.end(), 0.0); outIt.Set(sum / pixvals.size()); } else { auto finding = std::max_element(pixDistance.begin(), pixDistance.end()); outIt.Set(pixvals[std::distance(pixDistance.begin(), finding)]); } } else { outIt.Set(m_DefaultPixelValue); // default background value } progress.CompletedPixel(); ++outIt; } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > typename StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::PixelType StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::CastPixelWithBoundsChecking(const InterpolatorOutputType value, const ComponentType minComponent, const ComponentType maxComponent ) const { const unsigned int nComponents = InterpolatorConvertType::GetNumberOfComponents(value); PixelType outputValue; NumericTraits::SetLength( outputValue, nComponents ); for (unsigned int n = 0; n < nComponents; n++) { ComponentType component = InterpolatorConvertType::GetNthComponent( n, value ); if ( component < minComponent ) { PixelConvertType::SetNthComponent( n, outputValue, static_cast( minComponent ) ); } else if ( component > maxComponent ) { PixelConvertType::SetNthComponent( n, outputValue, static_cast( maxComponent ) ); } else { PixelConvertType::SetNthComponent(n, outputValue, static_cast( component ) ); } } return outputValue; } template typename StitchImageFilter::InputImageVectorType StitchImageFilter ::GetInputs() { InputImageVectorType inputs; for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); ++i) { auto input = this->GetInput(i); if (nullptr != input) { inputs.push_back(input); } } return inputs; } template typename StitchImageFilter::TransformMapType StitchImageFilter ::GetTransforms() { TransformMapType transforms; for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); ++i) { auto input = this->GetInput(i); auto transform = this->GetTransform(i); transforms[input] = transform; } return transforms; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GenerateInputRequestedRegion() { // Call the superclass' implementation of this method Superclass::GenerateInputRequestedRegion(); if ( !this->GetInput() ) { return; } // Get pointers to the input auto inputs = this->GetInputs(); for (auto& input : inputs) { InputImagePointer inputPtr = const_cast(input); // Determining the actual input region is non-trivial, especially // when we cannot assume anything about the transform being used. // So we do the easy thing and request the entire input image. // inputPtr->SetRequestedRegionToLargestPossibleRegion(); } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GenerateOutputInformation() { // Call the superclass' implementation of this method Superclass::GenerateOutputInformation(); // Get pointers to the input and output OutputImageType *outputPtr = this->GetOutput(); if ( !outputPtr ) { return; } const ReferenceImageBaseType *referenceImage = this->GetReferenceImage(); // Set the size of the output region if ( m_UseReferenceImage && referenceImage ) { outputPtr->SetLargestPossibleRegion( referenceImage->GetLargestPossibleRegion() ); } else { typename TOutputImage::RegionType outputLargestPossibleRegion; outputLargestPossibleRegion.SetSize(m_Size); outputLargestPossibleRegion.SetIndex(m_OutputStartIndex); outputPtr->SetLargestPossibleRegion(outputLargestPossibleRegion); } // Set spacing and origin if ( m_UseReferenceImage && referenceImage ) { outputPtr->SetSpacing( referenceImage->GetSpacing() ); outputPtr->SetOrigin( referenceImage->GetOrigin() ); outputPtr->SetDirection( referenceImage->GetDirection() ); } else { outputPtr->SetSpacing(m_OutputSpacing); outputPtr->SetOrigin(m_OutputOrigin); outputPtr->SetDirection(m_OutputDirection); } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > ModifiedTimeType StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GetMTime(void) const { ModifiedTimeType latestTime = Object::GetMTime(); for (const auto& interpolator : m_Interpolators) { if (interpolator.second.GetPointer()) { if (latestTime < interpolator.second->GetMTime()) { latestTime = interpolator.second->GetMTime(); } } } return latestTime; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "DefaultPixelValue: " << static_cast< typename NumericTraits< PixelType >::PrintType > ( m_DefaultPixelValue ) << std::endl; os << indent << "Size: " << m_Size << std::endl; os << indent << "OutputStartIndex: " << m_OutputStartIndex << std::endl; os << indent << "OutputSpacing: " << m_OutputSpacing << std::endl; os << indent << "OutputOrigin: " << m_OutputOrigin << std::endl; os << indent << "OutputDirection: " << m_OutputDirection << std::endl; for (const auto& interpolator : m_Interpolators) { os << indent << "Interpolator: " << interpolator.second.GetPointer() << std::endl; } os << indent << "UseReferenceImage: " << ( m_UseReferenceImage ? "On" : "Off" ) << std::endl; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::EnsureTransforms() { const auto inputCount = this->GetNumberOfIndexedInputs(); for (unsigned int i = 0; i < inputCount; ++i) { auto input = this->GetInput(i); if (nullptr == input) { itkExceptionMacro(<< "Nth input image is not set (n: " << i << ")."); } auto transform = this->GetTransform(i); if (nullptr == transform) { this->SetTransform(i, itk::IdentityTransform< TTransformPrecisionType, ImageDimension>::New().GetPointer()); } } } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > void StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::EnsureInterpolators() { const auto inputCount = this->GetNumberOfIndexedInputs(); InterpolatorMapType newInterpolatorMap; for (unsigned int i = 0; i < inputCount; ++i) { auto input = this->GetInput(i); if (nullptr == input) { itkExceptionMacro(<< "Nth input image is not set (n: " << i << ")."); } if (m_Interpolators[input].IsNull()) { newInterpolatorMap[input] = LinearInterpolatorType::New().GetPointer(); } else { newInterpolatorMap[input] = m_Interpolators[input]; } } m_Interpolators = newInterpolatorMap; } template< typename TInputImage, typename TOutputImage, typename TInterpolatorPrecisionType, typename TTransformPrecisionType > std::string StitchImageFilter< TInputImage, TOutputImage, TInterpolatorPrecisionType, TTransformPrecisionType > ::GetTransformInputName(unsigned int index) { return "transform_" + std::to_string(index); } } // end namespace itk #endif diff --git a/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp b/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp index e0fcb5f46a..1730a4f3ca 100644 --- a/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp +++ b/Modules/MatchPointRegistration/src/Helper/mitkImageStitchingHelper.cpp @@ -1,229 +1,229 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkImageStitchingHelper.h" #include #include #include #include #include #include #include #include #include #include #include "mapRegistration.h" #include "mitkRegistrationHelper.h" template typename ::itk::InterpolateImageFunction< TImage >::Pointer generateInterpolator(mitk::ImageMappingInterpolator::Type interpolatorType) { typedef ::itk::InterpolateImageFunction< TImage > BaseInterpolatorType; typename BaseInterpolatorType::Pointer result; switch (interpolatorType) { case mitk::ImageMappingInterpolator::NearestNeighbor: { result = ::itk::NearestNeighborInterpolateImageFunction::New(); break; } case mitk::ImageMappingInterpolator::BSpline_3: { typename ::itk::BSplineInterpolateImageFunction::Pointer spInterpolator = ::itk::BSplineInterpolateImageFunction::New(); spInterpolator->SetSplineOrder(3); result = spInterpolator; break; } case mitk::ImageMappingInterpolator::WSinc_Hamming: { result = ::itk::WindowedSincInterpolateImageFunction::New(); break; } case mitk::ImageMappingInterpolator::WSinc_Welch: { result = ::itk::WindowedSincInterpolateImageFunction >::New(); break; } default: { result = ::itk::LinearInterpolateImageFunction::New(); break; } } return result; }; template void doMITKStitching(const ::itk::Image* /*input1*/, mitk::Image::Pointer& result, std::vector inputs, std::vector<::map::core::RegistrationBase::ConstPointer> registrations, const mitk::BaseGeometry* resultGeometry, const double& paddingValue, itk::StitchStrategy stitchStrategy, mitk::ImageMappingInterpolator::Type interpolatorType) { using ConcreteRegistrationType = ::map::core::Registration; using ItkImageType = itk::Image; using StitchingFilterType = ::itk::StitchImageFilter; auto stitcher = StitchingFilterType::New(); stitcher->SetDefaultPixelValue(paddingValue); stitcher->SetOutputOrigin(resultGeometry->GetOrigin()); const auto spacing = resultGeometry->GetSpacing(); stitcher->SetOutputSpacing(spacing); typename StitchingFilterType::DirectionType itkDirection; const auto mitkDirection = resultGeometry->GetIndexToWorldTransform()->GetMatrix(); for (unsigned int i = 0; i < VImageDimension; ++i) { for (unsigned int j = 0; j < VImageDimension; ++j) { itkDirection[i][j] = mitkDirection[i][j] / spacing[j]; } } stitcher->SetOutputDirection(itkDirection); typename ItkImageType::SizeType size; size[0] = resultGeometry->GetExtent(0); size[1] = resultGeometry->GetExtent(1); size[2] = resultGeometry->GetExtent(2); stitcher->SetSize(size); - stitcher->SetNumberOfThreads(1); + stitcher->SetNumberOfWorkUnits(1); stitcher->SetStitchStrategy(stitchStrategy); auto inputIter = inputs.begin(); auto regIter = registrations.begin(); unsigned int index = 0; while (inputIter != inputs.end()) { auto itkInput = mitk::ImageToItkImage(*inputIter); auto castedReg = dynamic_cast(regIter->GetPointer()); auto kernel = dynamic_cast* >(&(castedReg->getInverseMapping())); if (nullptr == kernel) { mitkThrow() << "Cannot stitch images. At least passed registration object #"<SetInput(index, itkInput, kernel->getTransformModel(), generateInterpolator< ::itk::Image >(interpolatorType)); ++inputIter; ++regIter; ++index; } stitcher->Update(); mitk::CastToMitkImage<>(stitcher->GetOutput(),result); } mitk::Image::Pointer mitk::StitchImages(std::vector inputs, std::vector<::map::core::RegistrationBase::ConstPointer> registrations, const BaseGeometry* resultGeometry, const double& paddingValue, itk::StitchStrategy stitchStrategy, mitk::ImageMappingInterpolator::Type interpolatorType) { if (inputs.size() != registrations.size()) { mitkThrow() << "Cannot stitch images. Passed inputs vector and registrations vector have different sizes."; } if (inputs.empty()) { mitkThrow() << "Cannot stitch images. No input images are defined."; } auto inputDim = inputs.front()->GetDimension(); auto inputPixelType = inputs.front()->GetPixelType(); for (const auto& input : inputs) { if (input->GetDimension() != inputDim) { mitkThrow() << "Cannot stitch images. Images have different dimensions. Dimeonsion of first input: " << inputDim << "; wrong dimension: " << input->GetDimension(); } if (input->GetPixelType() != inputPixelType) { mitkThrow() << "Cannot stitch images. Input images have different pixeltypes. The current implementation does only support stitching of images with same pixel type. Dimeonsion of first input: " << inputPixelType.GetTypeAsString() << "; wrong dimension: " << input->GetPixelType().GetTypeAsString(); } if (input->GetTimeSteps() > 1) { mitkThrow() << "Cannot stitch dynamic images. At least one input image has multiple time steps."; } } for (const auto& reg : registrations) { if (reg->getMovingDimensions() != inputDim) { mitkThrow() << "Cannot stitch images. At least one registration has a different moving dimension then the inputs. Dimeonsion of inputs: " << inputDim << "; wrong dimension: " << reg->getMovingDimensions(); } if (reg->getTargetDimensions() != inputDim) { mitkThrow() << "Cannot stitch images. At least one registration has a different target dimension then the inputs. Dimeonsion of inputs: " << inputDim << "; wrong dimension: " << reg->getTargetDimensions(); } } Image::Pointer result; AccessFixedDimensionByItk_n(inputs.front(), doMITKStitching, 3, (result, inputs, registrations, resultGeometry, paddingValue, stitchStrategy, interpolatorType)); return result; } mitk::Image::Pointer mitk::StitchImages(std::vector inputs, std::vector registrations, const BaseGeometry* resultGeometry, const double& paddingValue, itk::StitchStrategy stitchStrategy, mitk::ImageMappingInterpolator::Type interpolatorType) { std::vector<::map::core::RegistrationBase::ConstPointer> unwrappedRegs; for (const auto& reg : registrations) { if (!reg) { mitkThrow() << "Cannot stitch images. At least one passed registration wrapper pointer is nullptr."; } unwrappedRegs.push_back(reg->GetRegistration()); } Image::Pointer result = StitchImages(inputs, unwrappedRegs, resultGeometry, paddingValue, stitchStrategy, interpolatorType); return result; } mitk::Image::Pointer mitk::StitchImages(std::vector inputs, const BaseGeometry* resultGeometry, const double& paddingValue, itk::StitchStrategy stitchStrategy, mitk::ImageMappingInterpolator::Type interpolatorType) { auto defaultReg = GenerateIdentityRegistration3D(); std::vector<::map::core::RegistrationBase::ConstPointer> defaultRegs; defaultRegs.resize(inputs.size()); std::fill(defaultRegs.begin(), defaultRegs.end(), defaultReg->GetRegistration()); Image::Pointer result = StitchImages(inputs, defaultRegs, resultGeometry, paddingValue, stitchStrategy, interpolatorType); return result; } diff --git a/Modules/ModelFit/include/itkMaskedStatisticsImageFilter.hxx b/Modules/ModelFit/include/itkMaskedStatisticsImageFilter.hxx index e6dea73005..63a3902636 100644 --- a/Modules/ModelFit/include/itkMaskedStatisticsImageFilter.hxx +++ b/Modules/ModelFit/include/itkMaskedStatisticsImageFilter.hxx @@ -1,393 +1,395 @@ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ #ifndef __itkMaskedStatisticsImageFilter_hxx #define __itkMaskedStatisticsImageFilter_hxx #include "itkMaskedStatisticsImageFilter.h" #include "itkImageScanlineIterator.h" #include "itkProgressReporter.h" namespace itk { template< typename TInputImage, typename TMaskImage > MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::MaskedStatisticsImageFilter():m_ThreadSum(1), m_SumOfSquares(1), m_Count(1), m_ThreadMin(1), m_ThreadMax(1) { + this->DynamicMultiThreadingOff(); + // first output is a copy of the image, DataObject created by // superclass // // allocate the data objects for the outputs which are // just decorators around pixel types for ( int i = 1; i < 3; ++i ) { typename PixelObjectType::Pointer output = static_cast< PixelObjectType * >( this->MakeOutput(i).GetPointer() ); this->ProcessObject::SetNthOutput( i, output.GetPointer() ); } // allocate the data objects for the outputs which are // just decorators around real types for ( int i = 3; i < 7; ++i ) { typename RealObjectType::Pointer output = static_cast< RealObjectType * >( this->MakeOutput(i).GetPointer() ); this->ProcessObject::SetNthOutput( i, output.GetPointer() ); } this->GetMinimumOutput()->Set( NumericTraits< PixelType >::max() ); this->GetMaximumOutput()->Set( NumericTraits< PixelType >::NonpositiveMin() ); this->GetMeanOutput()->Set( NumericTraits< RealType >::max() ); this->GetSigmaOutput()->Set( NumericTraits< RealType >::max() ); this->GetVarianceOutput()->Set( NumericTraits< RealType >::max() ); this->GetSumOutput()->Set(NumericTraits< RealType >::Zero); } template< typename TInputImage, typename TMaskImage > DataObject::Pointer MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::MakeOutput(DataObjectPointerArraySizeType output) { switch ( output ) { case 0: return TInputImage::New().GetPointer(); break; case 1: return PixelObjectType::New().GetPointer(); break; case 2: return PixelObjectType::New().GetPointer(); break; case 3: case 4: case 5: case 6: return RealObjectType::New().GetPointer(); break; default: // might as well make an image return TInputImage::New().GetPointer(); break; } } template< typename TInputImage, typename TMaskImage > typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::PixelObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetMinimumOutput() { return static_cast< PixelObjectType * >( this->ProcessObject::GetOutput(1) ); } template< typename TInputImage, typename TMaskImage > const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::PixelObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetMinimumOutput() const { return static_cast< const PixelObjectType * >( this->ProcessObject::GetOutput(1) ); } template< typename TInputImage, typename TMaskImage > typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::PixelObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetMaximumOutput() { return static_cast< PixelObjectType * >( this->ProcessObject::GetOutput(2) ); } template< typename TInputImage, typename TMaskImage > const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::PixelObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetMaximumOutput() const { return static_cast< const PixelObjectType * >( this->ProcessObject::GetOutput(2) ); } template< typename TInputImage, typename TMaskImage > typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetMeanOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(3) ); } template< typename TInputImage, typename TMaskImage > const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetMeanOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(3) ); } template< typename TInputImage, typename TMaskImage > typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetSigmaOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(4) ); } template< typename TInputImage, typename TMaskImage > const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetSigmaOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(4) ); } template< typename TInputImage, typename TMaskImage > typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetVarianceOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(5) ); } template< typename TInputImage, typename TMaskImage > const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetVarianceOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(5) ); } template< typename TInputImage, typename TMaskImage > typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetSumOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(6) ); } template< typename TInputImage, typename TMaskImage > const typename MaskedStatisticsImageFilter< TInputImage, TMaskImage >::RealObjectType * MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GetSumOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(6) ); } template< typename TInputImage, typename TMaskImage > void MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::GenerateInputRequestedRegion() { Superclass::GenerateInputRequestedRegion(); if ( this->GetInput() ) { InputImagePointer image = const_cast< typename Superclass::InputImageType * >( this->GetInput() ); image->SetRequestedRegionToLargestPossibleRegion(); } } template< typename TInputImage, typename TMaskImage > void MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::EnlargeOutputRequestedRegion(DataObject *data) { Superclass::EnlargeOutputRequestedRegion(data); data->SetRequestedRegionToLargestPossibleRegion(); } template< typename TInputImage, typename TMaskImage > void MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::AllocateOutputs() { // Pass the input through as the output InputImagePointer image = const_cast< TInputImage * >( this->GetInput() ); this->GraftOutput(image); // Nothing that needs to be allocated for the remaining outputs } template< typename TInputImage, typename TMaskImage > void MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::BeforeThreadedGenerateData() { - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); + ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits(); // Resize the thread temporaries m_Count.SetSize(numberOfThreads); m_SumOfSquares.SetSize(numberOfThreads); m_ThreadSum.SetSize(numberOfThreads); m_ThreadMin.SetSize(numberOfThreads); m_ThreadMax.SetSize(numberOfThreads); // Initialize the temporaries m_Count.Fill(NumericTraits< SizeValueType >::Zero); m_ThreadSum.Fill(NumericTraits< RealType >::Zero); m_SumOfSquares.Fill(NumericTraits< RealType >::Zero); m_ThreadMin.Fill( NumericTraits< PixelType >::max() ); m_ThreadMax.Fill( NumericTraits< PixelType >::NonpositiveMin() ); } template< typename TInputImage, typename TMaskImage > void MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::AfterThreadedGenerateData() { ThreadIdType i; SizeValueType count; RealType sumOfSquares; - ThreadIdType numberOfThreads = this->GetNumberOfThreads(); + ThreadIdType numberOfThreads = this->GetNumberOfWorkUnits(); PixelType minimum; PixelType maximum; RealType mean; RealType sigma; RealType variance; RealType sum; sum = sumOfSquares = NumericTraits< RealType >::Zero; count = 0; // Find the min/max over all threads and accumulate count, sum and // sum of squares minimum = NumericTraits< PixelType >::max(); maximum = NumericTraits< PixelType >::NonpositiveMin(); for ( i = 0; i < numberOfThreads; i++ ) { count += m_Count[i]; sum += m_ThreadSum[i]; sumOfSquares += m_SumOfSquares[i]; if ( m_ThreadMin[i] < minimum ) { minimum = m_ThreadMin[i]; } if ( m_ThreadMax[i] > maximum ) { maximum = m_ThreadMax[i]; } } // compute statistics mean = sum / static_cast< RealType >( count ); // unbiased estimate variance = ( sumOfSquares - ( sum * sum / static_cast< RealType >( count ) ) ) / ( static_cast< RealType >( count ) - 1 ); sigma = std::sqrt(variance); // Set the outputs this->GetMinimumOutput()->Set(minimum); this->GetMaximumOutput()->Set(maximum); this->GetMeanOutput()->Set(mean); this->GetSigmaOutput()->Set(sigma); this->GetVarianceOutput()->Set(variance); this->GetSumOutput()->Set(sum); } template< typename TInputImage, typename TMaskImage > void MaskedStatisticsImageFilter< TInputImage, TMaskImage > ::ThreadedGenerateData(const RegionType & outputRegionForThread, ThreadIdType threadId) { const SizeValueType size0 = outputRegionForThread.GetSize(0); if( size0 == 0) { return; } RealType realValue; PixelType value; RealType sum = NumericTraits< RealType >::Zero; RealType sumOfSquares = NumericTraits< RealType >::Zero; SizeValueType count = NumericTraits< SizeValueType >::Zero; PixelType min = NumericTraits< PixelType >::max(); PixelType max = NumericTraits< PixelType >::NonpositiveMin(); ImageScanlineConstIterator< TInputImage > it (this->GetInput(), outputRegionForThread); // support progress methods/callbacks const size_t numberOfLinesToProcess = outputRegionForThread.GetNumberOfPixels() / size0; ProgressReporter progress( this, threadId, numberOfLinesToProcess ); // do the work while ( !it.IsAtEnd() ) { while ( !it.IsAtEndOfLine() ) { bool isValid = true; if(m_Mask.IsNotNull()) { typename InputImageType::IndexType index = it.GetIndex(); typename InputImageType::PointType point; this->GetInput()->TransformIndexToPhysicalPoint(index, point); if (this->m_Mask->TransformPhysicalPointToIndex(point, index)) { isValid = this->m_Mask->GetPixel(index) > 0.0; }; } if (isValid) { value = it.Get(); realValue = static_cast< RealType >( value ); if ( value < min ) { min = value; } if ( value > max ) { max = value; } sum += realValue; sumOfSquares += ( realValue * realValue ); ++count; } ++it; } it.NextLine(); progress.CompletedPixel(); } m_ThreadSum[threadId] = sum; m_SumOfSquares[threadId] = sumOfSquares; m_Count[threadId] = count; m_ThreadMin[threadId] = min; m_ThreadMax[threadId] = max; } template< typename TImage, typename TMaskImage > void MaskedStatisticsImageFilter< TImage, TMaskImage > ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Minimum: " << static_cast< typename NumericTraits< PixelType >::PrintType >( this->GetMinimum() ) << std::endl; os << indent << "Maximum: " << static_cast< typename NumericTraits< PixelType >::PrintType >( this->GetMaximum() ) << std::endl; os << indent << "Sum: " << this->GetSum() << std::endl; os << indent << "Mean: " << this->GetMean() << std::endl; os << indent << "Sigma: " << this->GetSigma() << std::endl; os << indent << "Variance: " << this->GetVariance() << std::endl; } } // end namespace itk #endif diff --git a/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp b/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp index 09d16df573..6f2f9adb75 100644 --- a/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp +++ b/Modules/ModelFit/include/itkMultiOutputNaryFunctorImageFilter.tpp @@ -1,231 +1,233 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __itkMultiOutputNaryFunctorImageFilter_hxx #define __itkMultiOutputNaryFunctorImageFilter_hxx #include "itkMultiOutputNaryFunctorImageFilter.h" #include "itkImageRegionIterator.h" #include "itkProgressReporter.h" namespace itk { /** * Constructor */ template< class TInputImage, class TOutputImage, class TFunction, class TMaskImage > MultiOutputNaryFunctorImageFilter< TInputImage, TOutputImage, TFunction, TMaskImage > ::MultiOutputNaryFunctorImageFilter() { + this->DynamicMultiThreadingOff(); + // This number will be incremented each time an image // is added over the two minimum required this->SetNumberOfRequiredInputs(1); this->ActualizeOutputs(); } template< class TInputImage, class TOutputImage, class TFunction, class TMaskImage > void MultiOutputNaryFunctorImageFilter< TInputImage, TOutputImage, TFunction, TMaskImage > ::ActualizeOutputs() { this->SetNumberOfRequiredOutputs(m_Functor.GetNumberOfOutputs()); for (typename Superclass::DataObjectPointerArraySizeType i = this->GetNumberOfIndexedOutputs(); i< m_Functor.GetNumberOfOutputs(); ++i) { this->SetNthOutput( i, this->MakeOutput(i) ); } while(this->GetNumberOfIndexedOutputs() > m_Functor.GetNumberOfOutputs()) { this->RemoveOutput(this->GetNumberOfIndexedOutputs()-1); } }; /** * ThreadedGenerateData Performs the pixel-wise addition */ template< class TInputImage, class TOutputImage, class TFunction, class TMaskImage > void MultiOutputNaryFunctorImageFilter< TInputImage, TOutputImage, TFunction, TMaskImage > ::ThreadedGenerateData(const OutputImageRegionType & outputRegionForThread, ThreadIdType threadId) { ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() ); const unsigned int numberOfInputImages = static_cast< unsigned int >( this->GetNumberOfIndexedInputs() ); const unsigned int numberOfOutputImages = static_cast< unsigned int >( this->GetNumberOfIndexedOutputs() ); typedef ImageRegionConstIterator< TInputImage > ImageRegionConstIteratorType; std::vector< ImageRegionConstIteratorType * > inputItrVector; inputItrVector.reserve(numberOfInputImages); typedef ImageRegionIterator< TOutputImage > OutputImageRegionIteratorType; std::vector< OutputImageRegionIteratorType * > outputItrVector; outputItrVector.reserve(numberOfOutputImages); //check if mask image is set and generate iterator if mask is valid typedef ImageRegionConstIterator< TMaskImage > MaskImageRegionIteratorType; MaskImageRegionIteratorType* pMaskIterator = nullptr; if (m_Mask.IsNotNull()) { if (!m_Mask->GetLargestPossibleRegion().IsInside(outputRegionForThread)) { itkExceptionMacro("Mask of filter is set but does not cover region of thread. Mask region: "<< m_Mask->GetLargestPossibleRegion() <<"Thread region: "<( ProcessObject::GetInput(i) ); if ( inputPtr ) { inputItrVector.push_back( new ImageRegionConstIteratorType(inputPtr, outputRegionForThread) ); } } // go through the outputs and add iterators for non-null outputs for ( unsigned int i = 0; i < numberOfOutputImages; ++i ) { OutputImagePointer outputPtr = dynamic_cast< TOutputImage * >( ProcessObject::GetOutput(i) ); if ( outputPtr ) { outputItrVector.push_back( new OutputImageRegionIteratorType(outputPtr, outputRegionForThread) ); } } typename std::vector< ImageRegionConstIteratorType * >::iterator regionInputIterators; const typename std::vector< ImageRegionConstIteratorType * >::const_iterator regionInputItEnd = inputItrVector.end(); typename std::vector< OutputImageRegionIteratorType * >::iterator regionOutputIterators; const typename std::vector< OutputImageRegionIteratorType * >::const_iterator regionOutputItEnd = outputItrVector.end(); const unsigned int numberOfValidInputImages = inputItrVector.size(); const unsigned int numberOfValidOutputImages = outputItrVector.size(); if ( (numberOfValidInputImages != 0) && ( numberOfValidOutputImages != 0)) { try { while ( !(outputItrVector.front()->IsAtEnd()) ) { typename NaryInputArrayType::iterator arrayInIt; typename NaryOutputArrayType::iterator arrayOutIt; NaryInputArrayType naryInputArray(numberOfValidInputImages); NaryOutputArrayType naryOutputArray(numberOfValidOutputImages); bool isValid = true; if (pMaskIterator) { isValid = pMaskIterator->Get() > 0; ++(*pMaskIterator); } arrayInIt = naryInputArray.begin(); regionInputIterators = inputItrVector.begin(); typename ImageRegionConstIteratorType::IndexType currentIndex; if(regionInputIterators != regionInputItEnd) { currentIndex = ( *regionInputIterators )->GetIndex(); } while ( regionInputIterators != regionInputItEnd ) { *arrayInIt++ = ( *regionInputIterators )->Get(); ++( *( *regionInputIterators ) ); ++regionInputIterators; } if (isValid) { naryOutputArray = m_Functor(naryInputArray, currentIndex); if (numberOfValidOutputImages != naryOutputArray.size()) { itkExceptionMacro("Error. Number of valid output images do not equal number of outputs required by functor. Number of valid outputs: "<< numberOfValidOutputImages << "; needed output number:" << this->m_Functor.GetNumberOfOutputs()); } } else { for (typename NaryOutputArrayType::iterator pos = naryOutputArray.begin(); pos!= naryOutputArray.end(); ++pos) { *pos = 0.0; } } arrayOutIt = naryOutputArray.begin(); regionOutputIterators = outputItrVector.begin(); while ( regionOutputIterators != regionOutputItEnd ) { ( *regionOutputIterators )->Set(*arrayOutIt++); ++( *( *regionOutputIterators ) ); ++regionOutputIterators; } progress.CompletedPixel(); } } catch(...) { // Free memory in case of exceptions regionInputIterators = inputItrVector.begin(); while ( regionInputIterators != regionInputItEnd ) { delete ( *regionInputIterators++ ); } regionOutputIterators = outputItrVector.begin(); while ( regionOutputIterators != regionOutputItEnd ) { delete ( *regionOutputIterators++ ); } delete pMaskIterator; throw; } } // Free memory regulary regionInputIterators = inputItrVector.begin(); while ( regionInputIterators != regionInputItEnd ) { delete ( *regionInputIterators++ ); } regionOutputIterators = outputItrVector.begin(); while ( regionOutputIterators != regionOutputItEnd ) { delete ( *regionOutputIterators++ ); } delete pMaskIterator; } } // end namespace itk #endif diff --git a/Modules/ModelFit/include/mitkBinaryImageToLabelSetImageFilter.h b/Modules/ModelFit/include/mitkBinaryImageToLabelSetImageFilter.h index 9c817e68ab..534417bed8 100644 --- a/Modules/ModelFit/include/mitkBinaryImageToLabelSetImageFilter.h +++ b/Modules/ModelFit/include/mitkBinaryImageToLabelSetImageFilter.h @@ -1,64 +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 mitkBinaryImageToLabelSetImageFilter_h #define mitkBinaryImageToLabelSetImageFilter_h #include #include "mitkCommon.h" #include "MitkModelFitExports.h" namespace mitk { /** \brief Converts an binary image to a LabelSetImage. The amount of labels equals the connected components. */ class MITKMODELFIT_EXPORT BinaryImageToLabelSetImageFilter : public ImageToImageFilter { public: mitkClassMacro(BinaryImageToLabelSetImageFilter, ImageToImageFilter); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void SetInput(const InputImageType* image) override; void SetInput(unsigned int index, const InputImageType* image) override; itkSetMacro(FullyConnected, bool); itkGetConstMacro(FullyConnected, bool); itkSetMacro(ForegroundValue, unsigned int); itkGetConstMacro(ForegroundValue, unsigned int); itkSetMacro(OutputIsLabelSetImage, bool); itkGetConstMacro(OutputIsLabelSetImage, bool); private: using Superclass::SetInput; BinaryImageToLabelSetImageFilter() = default; ~BinaryImageToLabelSetImageFilter() override = default; template void ApplyBinaryImageToLabelMapFilter(const itk::Image* inputImage); void GenerateData() override; - void VerifyInputInformation() override; - void VerifyInputImage(const mitk::Image* inputImage); + void VerifyInputInformation() const override; + void VerifyInputImage(const mitk::Image* inputImage) const; bool m_FullyConnected = true; unsigned int m_ForegroundValue = 1; bool m_OutputIsLabelSetImage = false; }; } #endif diff --git a/Modules/ModelFit/include/mitkModelFitFunctorBase.h b/Modules/ModelFit/include/mitkModelFitFunctorBase.h index 9500c86954..2fae18e611 100644 --- a/Modules/ModelFit/include/mitkModelFitFunctorBase.h +++ b/Modules/ModelFit/include/mitkModelFitFunctorBase.h @@ -1,137 +1,139 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MODEL_FIT_FUNCTOR_BASE_H #define MODEL_FIT_FUNCTOR_BASE_H #include #include #include "mitkModelBase.h" #include "mitkSVModelFitCostFunction.h" #include "MitkModelFitExports.h" +#include + namespace mitk { class MITKMODELFIT_EXPORT ModelFitFunctorBase: public ::itk::Object { public: typedef ModelFitFunctorBase Self; typedef itk::Object Superclass; typedef itk::SmartPointer< Self > Pointer; typedef itk::SmartPointer< const Self > ConstPointer; itkTypeMacro(ModelFitFunctorBase, itk::Object); typedef ScalarType ParameterImagePixelType; typedef std::vector InputPixelArrayType; typedef std::vector OutputPixelArrayType; /** Returns the values determined by fitting the passed model. The values in the returned vector are ordered in the * following sequence: * - model parameters (see also GetParameterNames()) * - derived model parameters (see also GetDerivedParameterNames()) * - criterion(s) (see also GetCriterionNames()) * - evaluation parameters (see also GetEvaluationParameterNames()) * @param value Signal the model should be fitted onto * @param model Pointer to the preconfigured/ready to use model instance for the fitting against the signal curve * @param initialParameters parameters of the model that should be used as starting point of the fitting process. * @pre model must point to a valid instance. * @pre Size of initialParameters must be equal to model->GetNumberOfParameters(). */ OutputPixelArrayType Compute(const InputPixelArrayType& value, const ModelBase* model, const ModelBase::ParametersType& initialParameters) const; /** Returns the number of outputs the fit functor will return if compute is called. * The number depends in parts on the passed model. * @exception Exception will be thrown if no valid model is passed.*/ unsigned int GetNumberOfOutputs(const ModelBase* model) const; typedef ModelBase::ParameterNamesType ParameterNamesType; /** Returns names of all evaluation parameters defined by the user*/ ParameterNamesType GetEvaluationParameterNames() const; void ResetEvaluationParameters(); void RegisterEvaluationParameter(const std::string& parameterName, SVModelFitCostFunction* evaluationCostFunction); const SVModelFitCostFunction* GetEvaluationParameterCostFunction(const std::string& parameterName) const; /** Returns names of the criterion used to fit the model. */ virtual ParameterNamesType GetCriterionNames() const = 0 ; /** Returns names of the depug parameters generated by the functor. Is empty, if debug is deactivated. */ ParameterNamesType GetDebugParameterNames() const; itkBooleanMacro(DebugParameterMaps); itkSetMacro(DebugParameterMaps, bool); itkGetConstMacro(DebugParameterMaps, bool); protected: typedef ModelBase::ParametersType ParametersType; typedef ModelFitCostFunctionInterface::SignalType SignalType; ModelFitFunctorBase(); ~ModelFitFunctorBase() override; /**Internal Method called by Compute to get the final criterion values thar dove the fit. must be implemented be concrete functor classes.*/ virtual OutputPixelArrayType GetCriteria(const ModelBase* model, const ParametersType& parameters, const SignalType& sample) const = 0; /** Internal Method called by Compute(). Gets all derived parameters of the models with the final found parameters of the fit.*/ OutputPixelArrayType GetDerivedParameters(const ModelBase* model, const ParametersType& parameters) const; /** Internal Method called by Compute(). Gets the evaluation parameters for all cost functions enlisted by the user, based on the model with the final found parameters of the fit and the input signal.*/ OutputPixelArrayType GetEvaluationParameters(const ModelBase* model, const ParametersType& parameters, const SignalType& sample) const; typedef std::map DebugParameterMapType; /** Internal Method called by Compute(). It does the real fit and returns the found parameters. Additionally it must return its debug parameter via debugParameters. @post If m_DebugParameterMaps is true, it must return all debug parameters defined by GetDebugParameterNames() via debugParameters. @param value Signal the Model should be fitted against @param model Pointer to the model that should be fitted @param initialParameters Initial modal parameters for the fit @param [out] debugParameters Map containing all debug parameters for the done fit (must only valid if m_DebugParameterMap is true)*/ virtual ParametersType DoModelFit(const SignalType& value, const ModelBase* model, const ModelBase::ParametersType& initialParameters, DebugParameterMapType& debugParameters) const = 0; /** Returns names of the depug parameters generated by the functor. Will be called by GetDebugParameterNames, if debug is activated. */ virtual ParameterNamesType DefineDebugParameterNames()const = 0; private: typedef std::map CostFunctionMapType; CostFunctionMapType m_CostFunctionMap; bool m_DebugParameterMaps; - ::itk::SimpleFastMutexLock m_Mutex; + mutable std::mutex m_Mutex; }; } #endif // MODEL_FIT_FUNCTOR_BASE_H diff --git a/Modules/ModelFit/include/mitkModelFitInfo.h b/Modules/ModelFit/include/mitkModelFitInfo.h index b9e849c7b9..a554018f41 100644 --- a/Modules/ModelFit/include/mitkModelFitInfo.h +++ b/Modules/ModelFit/include/mitkModelFitInfo.h @@ -1,198 +1,195 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitkModelFitInfo_h #define mitkModelFitInfo_h -#include -#include - #include #include "mitkModelFitConstants.h" #include "mitkModelFitParameter.h" #include "mitkModelFitStaticParameterMap.h" #include "mitkScalarListLookupTable.h" #include "mitkModelParameterizerBase.h" #include "mitkModelTraitsInterface.h" #include "MitkModelFitExports.h" namespace mitk { namespace modelFit { /** * @brief Data class that stores all information about a modelfit that is relevant to the * visualization and stored as properties in the result nodes. */ class MITKMODELFIT_EXPORT ModelFitInfo : public itk::LightObject { public: typedef std::string UIDType; typedef std::vector ParamListType; typedef ParamListType::const_iterator ConstIterType; mitkClassMacroItkParent(ModelFitInfo, itk::LightObject); itkSimpleNewMacro(ModelFitInfo); ModelFitInfo() : x(mitk::ModelFitConstants::MODEL_X_VALUE_DEFAULT()), xAxisName(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT()), yAxisName(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT()) { } /** * @brief Adds the given parameter to this fit's parameter list if it doesn't * exist already. * @param p The param that should be added to this fit's parameter list. */ void AddParameter(Parameter::Pointer p); /** * @brief Searches for the parameter with the given name and type in the fit's * parameter list and returns it. * @param name The name of the desired parameter. * @param type The type of the desired parameter. * @return The parameter with the given name on success or NULL otherwise. */ Parameter::ConstPointer GetParameter(const std::string& name, const Parameter::Type& type) const; /** * @brief Searches for the parameter with the given name and type in the fit's * parameter list and deletes it if it exists. * @param name The name of the desired parameter. * @param type The type of the desired parameter. */ void DeleteParameter(const std::string& name, const Parameter::Type& type); /**Return const reference to the parameter list.*/ const ParamListType& GetParameters() const; /** ModelFitConstants::MODEL_NAME_PROPERTY_NAME */ std::string modelName; /** ModelFitConstants::MODEL_TYPE_PROPERTY_NAME */ std::string modelType; /** ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME */ std::string function; /** ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME */ std::string functionClassID; /** ModelFitConstants::MODEL_X_PROPERTY_NAME */ std::string x; /** ModelFitConstants::XAXIS_NAME_PROPERTY_NAME */ std::string xAxisName; /** ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME */ std::string xAxisUnit; /** ModelFitConstants::YAXIS_NAME_PROPERTY_NAME */ std::string yAxisName; /** ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME */ std::string yAxisUnit; /** ModelFitConstants::FIT_UID_PROPERTY_NAME */ UIDType uid; /** ModelFitConstants::FIT_NAME_PROPERTY_NAME */ std::string fitName; /** ModelFitConstants::FIT_TYPE_PROPERTY_NAME */ std::string fitType; /** ModelFitConstants::FIT_STATIC_PARAMETERS_PROPERTY_NAME */ StaticParameterMap staticParamMap; /** ModelFitConstants::FIT_INPUT_ROIUID_PROPERTY_NAME */ UIDType roiUID; /** ModelFitConstants::FIT_INPUT_DATA_PROPERTY_NAME */ ScalarListLookupTable inputData; mitk::Image::ConstPointer inputImage; private: typedef ParamListType::iterator IterType; - typedef itk::MutexLockHolder LockType; + typedef std::lock_guard LockType; ParamListType parameterList; - itk::SimpleFastMutexLock mutex; + std::mutex mutex; }; /** * @brief Reads the string property with the given name from the data of the given node * and returns its value. Throws a ModelFitException if the property doesn't exist. * @param node The node whose property value should be returned. * @param prop The name of the property that should be read. * @return The value of the found property. * @throw ModelFitException If the property doesn't exist or returns an empty string. */ MITKMODELFIT_EXPORT const std::string GetMandatoryProperty(const mitk::DataNode* node, const std::string& prop); /** * @brief Reads the string property with the given name from the given base data and * returns its value. Throws a ModelFitException if the property doesn't exist. * @param data The data whose property value should be returned. * @param prop The name of the property that should be read. * @return The value of the found property. * @throw ModelFitException If the property doesn't exist or returns an empty string. */ MITKMODELFIT_EXPORT const std::string GetMandatoryProperty(const mitk::BaseData* data, const std::string& prop); /** * @brief Creates a new ModelFitInfo instance from the nodes in the passed storage. * The fit will be identified by the passed UID. Returns the instance on * success. * @param uid The uid of the fit that should get its ModelFitInfo created and which identifies the nodes in the storage. * @param storage Pointer to the data storage containing any potential relevantThe nodes. * @return The newly created modelfit on success or NULL otherwise. */ MITKMODELFIT_EXPORT ModelFitInfo::Pointer CreateFitInfoFromNode(const ModelFitInfo::UIDType& uid, const mitk::DataStorage* storage); /** creates a new ModelFitInfo instance from a passed modal instance and his traits instance* * @param usedParameterizer Pointer to a model which was used for a fit, which should get a fit info created. * @param inputImage Pointer to the input image. If it has no UID yet, a property will be added to the node. * @param fitType String identifying the type of the fit (e.g. ROI based or voxel based) * @param fitName Optional human readable name of the fit. * @param roiUID UID of the ROI, if one was used. * @return The newly created modelfit on success or NULL otherwise.*/ MITKMODELFIT_EXPORT ModelFitInfo::Pointer CreateFitInfoFromModelParameterizer( const ModelParameterizerBase* usedParameterizer, mitk::BaseData* inputImage, const std::string& fitType, const std::string& fitName = "", const ModelFitInfo::UIDType& roiUID = ""); /** @overload Overloaded version that allows additional definition of optional input data for the fit.*/ MITKMODELFIT_EXPORT ModelFitInfo::Pointer CreateFitInfoFromModelParameterizer( const ModelParameterizerBase* usedParameterizer, mitk::BaseData* inputImage, const std::string& fitType, const ScalarListLookupTable& inputData, const std::string& fitName = "", const ModelFitInfo::UIDType& roiUID = ""); /** Returns all nodes that belong to the fit indicated by the passed UID. * @param fitUID The uid of the fit that is relevant for the query. * @param storage Pointer to the data storage containing any potential relevant nodes. * @return The set of found nodes or null if storage is not valid. */ MITKMODELFIT_EXPORT DataStorage::SetOfObjects::ConstPointer GetNodesOfFit( const ModelFitInfo::UIDType& fitUID, const mitk::DataStorage* storage); typedef std::set NodeUIDSetType; /** Returns the UIDs of all fits that are derived (directly or indirectly from the passed node). * @param node The node which defines the parent node. It will be searched in his derived nodes for fits. * @param storage Pointer to the data storage containing any potential relevant nodes. * @return The set of found uid will be returned. */ MITKMODELFIT_EXPORT NodeUIDSetType GetFitUIDsOfNode(const mitk::DataNode* node, const mitk::DataStorage* storage); } } #endif // mitkModelFit_h diff --git a/Modules/ModelFit/src/Common/mitkBinaryImageToLabelSetImageFilter.cpp b/Modules/ModelFit/src/Common/mitkBinaryImageToLabelSetImageFilter.cpp index 577374108c..f23020706a 100644 --- a/Modules/ModelFit/src/Common/mitkBinaryImageToLabelSetImageFilter.cpp +++ b/Modules/ModelFit/src/Common/mitkBinaryImageToLabelSetImageFilter.cpp @@ -1,96 +1,96 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkBinaryImageToLabelSetImageFilter.h" #include #include #include #include //itk #include #include template void mitk::BinaryImageToLabelSetImageFilter::ApplyBinaryImageToLabelMapFilter(const itk::Image* inputImage) { using ImageType = itk::Image; using BinaryImageToLabelMapFilterType = itk::BinaryImageToLabelMapFilter; typename BinaryImageToLabelMapFilterType::Pointer binaryImageToLabelMapFilter = BinaryImageToLabelMapFilterType::New(); binaryImageToLabelMapFilter->SetInput(inputImage); binaryImageToLabelMapFilter->SetInputForegroundValue(m_ForegroundValue); binaryImageToLabelMapFilter->SetFullyConnected(m_FullyConnected); using LabelMap2ImageType = itk::LabelMapToLabelImageFilter< typename BinaryImageToLabelMapFilterType::OutputImageType, ImageType>; typename LabelMap2ImageType::Pointer label2image = LabelMap2ImageType::New(); label2image->SetInput(binaryImageToLabelMapFilter->GetOutput()); label2image->Update(); auto labeledImage = mitk::ImportItkImage(label2image->GetOutput()); if (m_OutputIsLabelSetImage) { auto labeledBinaryImage = mitk::LabelSetImage::New(); labeledBinaryImage->InitializeByLabeledImage(labeledImage); this->SetOutput(MakeNameFromOutputIndex(0), labeledBinaryImage.GetPointer()); } else { this->SetOutput(MakeNameFromOutputIndex(0), labeledImage.GetPointer()); } } - void mitk::BinaryImageToLabelSetImageFilter::VerifyInputImage(const mitk::Image* inputImage) + void mitk::BinaryImageToLabelSetImageFilter::VerifyInputImage(const mitk::Image* inputImage) const { if (!inputImage->IsInitialized()) mitkThrow() << "Input image is not initialized."; if (!inputImage->IsVolumeSet()) mitkThrow() << "Input image volume is not set."; auto geometry = inputImage->GetGeometry(); if (nullptr == geometry || !geometry->IsValid()) mitkThrow() << "Input image has invalid geometry."; if (!geometry->GetImageGeometry()) mitkThrow() << "Geometry of input image is not an image geometry."; } void mitk::BinaryImageToLabelSetImageFilter::GenerateData() { const auto* inputImage = this->GetInput(); AccessByItk(inputImage, ApplyBinaryImageToLabelMapFilter); } void mitk::BinaryImageToLabelSetImageFilter::SetInput(const InputImageType* image) { if (this->GetInput() == image) return; Superclass::SetInput(image); } void mitk::BinaryImageToLabelSetImageFilter::SetInput(unsigned int index, const InputImageType* image) { if (0 != index) mitkThrow() << "Input index " << index << " is invalid."; this->SetInput(image); } -void mitk::BinaryImageToLabelSetImageFilter::VerifyInputInformation() +void mitk::BinaryImageToLabelSetImageFilter::VerifyInputInformation() const { Superclass::VerifyInputInformation(); VerifyInputImage(this->GetInput()); } diff --git a/Modules/ModelFit/src/Common/mitkTimeGridHelper.cpp b/Modules/ModelFit/src/Common/mitkTimeGridHelper.cpp index 6d65d1d2eb..416439ae6f 100644 --- a/Modules/ModelFit/src/Common/mitkTimeGridHelper.cpp +++ b/Modules/ModelFit/src/Common/mitkTimeGridHelper.cpp @@ -1,89 +1,89 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkTimeGridHelper.h" -#include "itkExceptionObject.h" +#include "itkMacro.h" bool mitk::TimeGridIsMonotonIncreasing(const mitk::ModelBase::TimeGridType timeGrid) { const auto beginPos = timeGrid.begin(); const auto endPos = timeGrid.end(); for(mitk::ModelBase::TimeGridType::const_iterator posTime = beginPos; posTime != endPos; ++posTime) { if (posTime != beginPos && *(posTime-1)<*posTime) return false; } return true; }; mitk::ModelBase::ModelResultType mitk::InterpolateSignalToNewTimeGrid(const ModelBase::ModelResultType& inputSignal, const ModelBase::TimeGridType& inputGrid, const ModelBase::TimeGridType& outputGrid) { mitk::ModelBase::ModelResultType result(outputGrid.GetSize()); if (! inputSignal.GetSize()) { return result; } if (inputSignal.GetSize() != inputGrid.GetSize()) { itkGenericExceptionMacro("Input signal and input time grid have not the same size."); } mitk::ModelBase::ModelResultType::ValueType lastValue = inputSignal[0]; mitk::ModelBase::TimeGridType::ValueType lastTime = itk::NumericTraits::NonpositiveMin(); mitk::ModelBase::TimeGridType::const_iterator posITime = inputGrid.begin(); mitk::ModelBase::ModelResultType::const_iterator posValue = inputSignal.begin(); mitk::ModelBase::ModelResultType::iterator posResult = result.begin(); for(mitk::ModelBase::TimeGridType::const_iterator posOTime = outputGrid.begin(); posOTime != outputGrid.end(); ++posResult, ++posOTime) { while(posITime!=inputGrid.end() && *posOTime > *posITime) { //forward in the input grid until the current output point //is between last and the current input point. lastValue = *posValue; lastTime = *posITime; ++posValue; ++posITime; } double weightLast = 1 - (*posOTime - lastTime)/(*posITime - lastTime); double weightNext = 1 - (*posITime - *posOTime)/(*posITime - lastTime); *posResult = weightLast * lastValue + weightNext * (*posValue); } return result; }; mitk::ModelBase::TimeGridType mitk::GenerateSupersampledTimeGrid(const mitk::ModelBase::TimeGridType& grid, const unsigned int samplingRate) { unsigned int origGridSize = grid.size(); mitk::ModelBase::TimeGridType interpolatedTimeGrid(((origGridSize - 1) * samplingRate) + 1); for (unsigned int t = 0; t < origGridSize - 1; ++t) { double delta = (grid[t + 1] - grid[t]) / samplingRate; for (unsigned int i = 0; i < samplingRate; ++i) { interpolatedTimeGrid[(t * samplingRate) + i] = grid[t] + i * delta; } } interpolatedTimeGrid[interpolatedTimeGrid.size() - 1] = grid[grid.size() - 1]; return interpolatedTimeGrid; }; diff --git a/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp b/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp index 7e025c4a21..1467eabd61 100644 --- a/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp +++ b/Modules/ModelFit/src/Functors/mitkModelFitFunctorBase.cpp @@ -1,241 +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. ============================================================================*/ #include "mitkModelFitFunctorBase.h" mitk::ModelFitFunctorBase::OutputPixelArrayType mitk::ModelFitFunctorBase:: Compute(const InputPixelArrayType& value, const ModelBase* model, const ModelBase::ParametersType& initialParameters) const { if (!model) { itkExceptionMacro("Cannot compute fit. Passed model is not defined."); } if (model->GetNumberOfParameters() != initialParameters.Size()) { itkExceptionMacro("Cannot compute fit. Parameter count of passed model and passed initial parameters differ. Model parameter count: " << model->GetNumberOfParameters() << "; Initial parameters: " << initialParameters); } SignalType sample(value.size()); for (SignalType::SizeValueType i = 0; i < sample.Size(); ++i) { sample[i] = value [i]; } DebugParameterMapType debugParams; ParameterNamesType debugNames; if (this->m_DebugParameterMaps) { debugNames = this->GetDebugParameterNames(); } ParametersType fittedParameters = DoModelFit(sample, model, initialParameters, debugParams); OutputPixelArrayType derivedParameters = this->GetDerivedParameters(model, fittedParameters); OutputPixelArrayType criteria = this->GetCriteria(model, fittedParameters, sample); OutputPixelArrayType evaluationParameters = this->GetEvaluationParameters(model, fittedParameters, sample); if (criteria.size() != this->GetCriterionNames().size()) { itkExceptionMacro("ModelFitInfo implementation seems to be inconsitent. Number of criterion values is not equal to number of criterion names."); } OutputPixelArrayType result(fittedParameters.Size() + derivedParameters.size() + criteria.size() + evaluationParameters.size() + debugNames.size()); for (ParametersType::SizeValueType i = 0; i < fittedParameters.Size(); ++i) { result[i] = fittedParameters[i]; } OutputPixelArrayType::size_type offset = fittedParameters.Size(); for (OutputPixelArrayType::size_type j = 0; j < derivedParameters.size(); ++j) { result[offset + j] = derivedParameters[j]; } offset += derivedParameters.size(); for (OutputPixelArrayType::size_type j = 0; j < criteria.size(); ++j) { result[offset + j] = criteria[j]; } offset += criteria.size(); for (OutputPixelArrayType::size_type j = 0; j < evaluationParameters.size(); ++j) { result[offset + j] = evaluationParameters[j]; } offset += evaluationParameters.size(); for (OutputPixelArrayType::size_type j = 0; j < debugNames.size(); ++j) { DebugParameterMapType::const_iterator pos = debugParams.find(debugNames[j]); if (pos == debugParams.end()) { itkExceptionMacro("ModelFitInfo implementation seems to be inconsitent. Debug parameter defined by functor is not in its returned debug map. Invalid debug parameter name: "<second; } } return result; }; unsigned int mitk::ModelFitFunctorBase::GetNumberOfOutputs(const ModelBase* model) const { if (!model) { itkExceptionMacro("Cannot get number of outputs. Model is not defined."); } return model->GetNumberOfParameters() + model->GetNumberOfDerivedParameters() + this->GetCriterionNames().size() + m_CostFunctionMap.size()+ this->GetDebugParameterNames().size(); }; void mitk::ModelFitFunctorBase::ResetEvaluationParameters() { - m_Mutex.Lock(); + m_Mutex.lock(); m_CostFunctionMap.clear(); - m_Mutex.Unlock(); + m_Mutex.unlock(); }; void mitk::ModelFitFunctorBase::RegisterEvaluationParameter(const std::string& parameterName, SVModelFitCostFunction* evaluationCostFunction) { - m_Mutex.Lock(); + m_Mutex.lock(); SVModelFitCostFunction::Pointer costFunctPtr = evaluationCostFunction; m_CostFunctionMap.insert(std::make_pair(parameterName, costFunctPtr)); - m_Mutex.Unlock(); + m_Mutex.unlock(); }; mitk::ModelFitFunctorBase::ParameterNamesType mitk::ModelFitFunctorBase::GetEvaluationParameterNames() const { - m_Mutex.Lock(); + m_Mutex.lock(); ParameterNamesType result; for (CostFunctionMapType::const_iterator pos = m_CostFunctionMap.begin(); pos != m_CostFunctionMap.end(); ++pos) { result.push_back(pos->first); } - m_Mutex.Unlock(); + m_Mutex.unlock(); return result; }; const mitk::SVModelFitCostFunction* mitk::ModelFitFunctorBase::GetEvaluationParameterCostFunction(const std::string& parameterName) const { const SVModelFitCostFunction* result = nullptr; - m_Mutex.Lock(); + m_Mutex.lock(); CostFunctionMapType::const_iterator pos = m_CostFunctionMap.find(parameterName); if (pos != m_CostFunctionMap.end()) { result = (pos->second).GetPointer(); } - m_Mutex.Unlock(); + m_Mutex.unlock(); return result; }; mitk::ModelFitFunctorBase::ParameterNamesType mitk::ModelFitFunctorBase::GetDebugParameterNames() const { ParameterNamesType result; if (this->m_DebugParameterMaps) { result = this->DefineDebugParameterNames(); } return result; }; mitk::ModelFitFunctorBase:: ModelFitFunctorBase() : m_DebugParameterMaps(false) {}; mitk::ModelFitFunctorBase:: ~ModelFitFunctorBase() {}; mitk::ModelFitFunctorBase::OutputPixelArrayType mitk::ModelFitFunctorBase::GetDerivedParameters(const ModelBase* model, const ParametersType& parameters) const { ModelBase::DerivedParameterMapType derivedParameterMap = model->GetDerivedParameters(parameters); OutputPixelArrayType result(derivedParameterMap.size()); unsigned int i = 0; for (ModelBase::DerivedParameterMapType::const_iterator pos = derivedParameterMap.begin(); pos != derivedParameterMap.end(); ++pos, ++i) { result[i] = pos->second; } return result; }; mitk::ModelFitFunctorBase::OutputPixelArrayType mitk::ModelFitFunctorBase::GetEvaluationParameters(const ModelBase* model, const ParametersType& parameters, const SignalType& sample) const { - m_Mutex.Lock(); + m_Mutex.lock(); OutputPixelArrayType result(m_CostFunctionMap.size()); unsigned int i = 0; for (CostFunctionMapType::const_iterator pos = m_CostFunctionMap.begin(); pos != m_CostFunctionMap.end(); ++pos, ++i) { //break constness to configure evaluation cost functions. This operatoin is guarded be the mutex //after costFct->GetValue() the cost function may change its state again and is irrelevant for the //current call of GetEvaluationParameters SVModelFitCostFunction* costFct = const_cast(pos->second.GetPointer()); costFct->SetModel(model); costFct->SetSample(sample); result[i] = costFct->GetValue(parameters); } - m_Mutex.Unlock(); + m_Mutex.unlock(); return result; }; diff --git a/Modules/ModelFit/test/itkMaskedStatisticsImageFilterTest.cpp b/Modules/ModelFit/test/itkMaskedStatisticsImageFilterTest.cpp index 5a00710a8e..590937f13f 100644 --- a/Modules/ModelFit/test/itkMaskedStatisticsImageFilterTest.cpp +++ b/Modules/ModelFit/test/itkMaskedStatisticsImageFilterTest.cpp @@ -1,76 +1,76 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "itkImage.h" #include "itkImageRegionIterator.h" #include "itkMaskedStatisticsImageFilter.h" #include "mitkTestingMacros.h" #include "mitkVector.h" #include "mitkTestDynamicImageGenerator.h" int itkMaskedStatisticsImageFilterTest(int /*argc*/, char*[] /*argv[]*/) { // always start with this! MITK_TEST_BEGIN("itkMaskedStatisticsImageFilterTest") //Prepare test artifacts and helper mitk::TestImageType::Pointer img1 = mitk::GenerateTestImage(); typedef itk::MaskedStatisticsImageFilter FilterType; FilterType::Pointer testFilter = FilterType::New(); testFilter->SetInput(img1); - testFilter->SetNumberOfThreads(2); + testFilter->SetNumberOfWorkUnits(2); testFilter->Update(); FilterType::PixelType max = testFilter->GetMaximum(); FilterType::PixelType min = testFilter->GetMinimum(); FilterType::RealType mean = testFilter->GetMean(); FilterType::RealType sig = testFilter->GetSigma(); FilterType::RealType variance = testFilter->GetVariance(); FilterType::RealType sum = testFilter->GetSum(); CPPUNIT_ASSERT_MESSAGE("Check computed maximum",9 == max); CPPUNIT_ASSERT_MESSAGE("Check computed minimum",1 == min); CPPUNIT_ASSERT_MESSAGE("Check computed mean",5 == mean); CPPUNIT_ASSERT_MESSAGE("Check computed sigma",sqrt(7.5) == sig); CPPUNIT_ASSERT_MESSAGE("Check computed variance",7.5 == variance); CPPUNIT_ASSERT_MESSAGE("Check computed sum",45 == sum); //Test with mask set mitk::TestMaskType::Pointer mask = mitk::GenerateTestMask(); testFilter->SetMask(mask); testFilter->Update(); max = testFilter->GetMaximum(); min = testFilter->GetMinimum(); mean = testFilter->GetMean(); sig = testFilter->GetSigma(); variance = testFilter->GetVariance(); sum = testFilter->GetSum(); CPPUNIT_ASSERT_MESSAGE("Check computed maximum",4 == max); CPPUNIT_ASSERT_MESSAGE("Check computed minimum",2 == min); CPPUNIT_ASSERT_MESSAGE("Check computed mean",3 == mean); CPPUNIT_ASSERT_MESSAGE("Check computed sigma",1 == sig); CPPUNIT_ASSERT_MESSAGE("Check computed variance",1 == variance); CPPUNIT_ASSERT_MESSAGE("Check computed sum",9 == sum); MITK_TEST_END() } diff --git a/Modules/ModelFit/test/itkMultiOutputNaryFunctorImageFilterTest.cpp b/Modules/ModelFit/test/itkMultiOutputNaryFunctorImageFilterTest.cpp index f38866b609..4ca0102a9e 100644 --- a/Modules/ModelFit/test/itkMultiOutputNaryFunctorImageFilterTest.cpp +++ b/Modules/ModelFit/test/itkMultiOutputNaryFunctorImageFilterTest.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. ============================================================================*/ #include "itkImage.h" #include "itkImageRegionIterator.h" #include "itkMultiOutputNaryFunctorImageFilter.h" #include "mitkTestingMacros.h" #include "mitkVector.h" #include "mitkTestDynamicImageGenerator.h" class TestFunctor { public: typedef std::vector InputPixelArrayType; typedef std::vector OutputPixelArrayType; typedef itk::Index<2> IndexType; TestFunctor() { secondOutputSelection = 0; }; ~TestFunctor() {}; int secondOutputSelection; unsigned int GetNumberOfOutputs() const { return 4; } bool operator!=( const TestFunctor & other) const { return !(*this == other); } bool operator==( const TestFunctor & other ) const { return secondOutputSelection == other.secondOutputSelection; } inline OutputPixelArrayType operator()( const InputPixelArrayType & value, const IndexType& currentIndex ) const { OutputPixelArrayType result; int sum = 0; for (InputPixelArrayType::const_iterator pos = value.begin(); pos != value.end(); ++pos) { sum += *pos; } result.push_back(sum); result.push_back(value[secondOutputSelection]); result.push_back(currentIndex[0]); result.push_back(currentIndex[1]); return result; } }; int itkMultiOutputNaryFunctorImageFilterTest(int /*argc*/, char*[] /*argv[]*/) { // always start with this! MITK_TEST_BEGIN("itkMultiOutputNaryFunctorImageFilter") //Prepare test artifacts and helper mitk::TestImageType::Pointer img1 = mitk::GenerateTestImage(); mitk::TestImageType::Pointer img2 = mitk::GenerateTestImage(10); mitk::TestImageType::Pointer img3 = mitk::GenerateTestImage(100); mitk::TestImageType::IndexType testIndex1; testIndex1[0] = 0; testIndex1[1] = 0; mitk::TestImageType::IndexType testIndex2; testIndex2[0] = 2; testIndex2[1] = 0; mitk::TestImageType::IndexType testIndex3; testIndex3[0] = 0; testIndex3[1] = 1; mitk::TestImageType::IndexType testIndex4; testIndex4[0] = 1; testIndex4[1] = 1; mitk::TestImageType::IndexType testIndex5; testIndex5[0] = 2; testIndex5[1] = 2; //Test default usage of filter typedef itk::MultiOutputNaryFunctorImageFilter FilterType; FilterType::Pointer testFilter = FilterType::New(); testFilter->SetInput(0,img1); testFilter->SetInput(1,img2); testFilter->SetInput(2,img3); - testFilter->SetNumberOfThreads(2); + testFilter->SetNumberOfWorkUnits(2); testFilter->Update(); mitk::TestImageType::Pointer out1 = testFilter->GetOutput(0); mitk::TestImageType::Pointer out2 = testFilter->GetOutput(1); mitk::TestImageType::Pointer out3 = testFilter->GetOutput(2); mitk::TestImageType::Pointer out4 = testFilter->GetOutput(3); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #1 (functor #1)",111 == out1->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #2 (functor #1)",333 == out1->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #3 (functor #1)",444 == out1->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #4 (functor #1)",555 == out1->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #5 (functor #1)",999 == out1->GetPixel(testIndex5)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #1 (functor #1)",1 == out2->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #2 (functor #1)",3 == out2->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #3 (functor #1)",4 == out2->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #4 (functor #1)",5 == out2->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #5 (functor #1)",9 == out2->GetPixel(testIndex5)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #1 (functor #1)",0 == out3->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #2 (functor #1)",2 == out3->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #3 (functor #1)",0 == out3->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #4 (functor #1)",1 == out3->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #5 (functor #1)",2 == out3->GetPixel(testIndex5)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #1 (functor #1)",0 == out4->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #2 (functor #1)",0 == out4->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #3 (functor #1)",1 == out4->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #4 (functor #1)",1 == out4->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #5 (functor #1)",2 == out4->GetPixel(testIndex5)); //Test with functor set by user TestFunctor funct2; funct2.secondOutputSelection = 1; testFilter->SetFunctor(funct2); testFilter->Update(); out1 = testFilter->GetOutput(0); out2 = testFilter->GetOutput(1); out3 = testFilter->GetOutput(2); out4 = testFilter->GetOutput(3); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #1 (functor #2)",111 == out1->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #2 (functor #2)",333 == out1->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #3 (functor #2)",444 == out1->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #4 (functor #2)",555 == out1->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #1 index #5 (functor #2)",999 == out1->GetPixel(testIndex5)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #1 (functor #2)",10 == out2->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #2 (functor #2)",30 == out2->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #3 (functor #2)",40 == out2->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #4 (functor #2)",50 == out2->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #2 index #5 (functor #2)",90 == out2->GetPixel(testIndex5)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #1 (functor #2)",0 == out3->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #2 (functor #2)",2 == out3->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #3 (functor #2)",0 == out3->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #4 (functor #2)",1 == out3->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #3 index #5 (functor #2)",2 == out3->GetPixel(testIndex5)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #1 (functor #2)",0 == out4->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #2 (functor #2)",0 == out4->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #3 (functor #2)",1 == out4->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #4 (functor #2)",1 == out4->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of output #4 index #5 (functor #2)",2 == out4->GetPixel(testIndex5)); //Test with mask set mitk::TestMaskType::Pointer mask = mitk::GenerateTestMask(); testFilter->SetMask(mask); testFilter->Update(); out1 = testFilter->GetOutput(0); out2 = testFilter->GetOutput(1); out3 = testFilter->GetOutput(2); out4 = testFilter->GetOutput(3); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #1 index #1 (functor #2)",0 == out1->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #1 index #2 (functor #2)",333 == out1->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #1 index #3 (functor #2)",444 == out1->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #1 index #4 (functor #2)",0 == out1->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #1 index #5 (functor #2)",0 == out1->GetPixel(testIndex5)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #2 index #1 (functor #2)",0 == out2->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #2 index #2 (functor #2)",30 == out2->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #2 index #3 (functor #2)",40 == out2->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #2 index #4 (functor #2)",0 == out2->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #2 index #5 (functor #2)",0 == out2->GetPixel(testIndex5)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #3 index #1 (functor #2)",0 == out3->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #3 index #2 (functor #2)",2 == out3->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #3 index #3 (functor #2)",0 == out3->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #3 index #4 (functor #2)",0 == out3->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #3 index #5 (functor #2)",0 == out3->GetPixel(testIndex5)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #4 index #1 (functor #2)",0 == out4->GetPixel(testIndex1)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #4 index #2 (functor #2)",0 == out4->GetPixel(testIndex2)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #4 index #3 (functor #2)",1 == out4->GetPixel(testIndex3)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #4 index #4 (functor #2)",0 == out4->GetPixel(testIndex4)); CPPUNIT_ASSERT_MESSAGE("Check pixel of masked output #4 index #5 (functor #2)",0 == out4->GetPixel(testIndex5)); MITK_TEST_END() } diff --git a/Modules/Multilabel/CMakeLists.txt b/Modules/Multilabel/CMakeLists.txt index 51a2a06fd3..ff1f469fad 100644 --- a/Modules/Multilabel/CMakeLists.txt +++ b/Modules/Multilabel/CMakeLists.txt @@ -1,10 +1,11 @@ mitk_create_module( DEPENDS MitkCore MitkAlgorithmsExt MitkSceneSerializationBase MitkDICOMQI + PACKAGE_DEPENDS ITK|Smoothing ) add_subdirectory(autoload/IO) add_subdirectory(autoload/DICOMSegIO) if(BUILD_TESTING) add_subdirectory(Testing) endif() diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp index da0754faf6..25f2bd524d 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp @@ -1,654 +1,654 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkLabelSetImageWriter__cpp #define __mitkLabelSetImageWriter__cpp #include "mitkLabelSetImageIO.h" #include "mitkBasePropertySerializer.h" #include "mitkIOMimeTypes.h" #include "mitkImageAccessByItk.h" #include "mitkLabelSetIOHelper.h" #include "mitkLabelSetImageConverter.h" #include #include #include #include #include #include // itk #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include namespace mitk { const char* const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char* const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char* const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char* const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; const char* const PROPERTY_KEY_UID = "org_mitk_uid"; 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->SetComponentType(static_cast(pixelType.GetComponentType()) < PixelComponentUserType + ? pixelType.GetComponentType() + : itk::IOComponentEnum::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)); + mitk::Vector3D mitkDirection(0.0); + mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref()); 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()) { tinyxml2::XMLDocument document; document.InsertEndChild(document.NewDeclaration()); auto *labelElem = mitk::LabelSetIOHelper::GetLabelAsXMLElement(document, iter->second); document.InsertEndChild(labelElem); tinyxml2::XMLPrinter printer; document.Print(&printer); sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.CStr()); ++iter; ++count; } } // end label set specific meta data // Handle time geometry const auto* arbitraryTG = dynamic_cast(input->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG); nrrdImageIo->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = input->GetPropertyList(); for (const auto& property : *imagePropertyList->GetMap()) { mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = infoList.front()->GetSerializationFunction()(property.second); if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), key, value); } // Handle UID itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_UID, input->GetUID()); ImageReadAccessor imageAccess(inputVector); nrrdImageIo->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } // end image write } IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileName(fileName); io->ReadImageInformation(); itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary(); std::string value(""); itk::ExposeMetaData(imgMetaDataDictionary, "modality", value); if (value.compare("org.mitk.image.multilabel") == 0) { return Supported; } else return Unsupported; } std::vector LabelSetImageIO::DoRead() { mitk::LocaleSwitch localeSwitch("C"); // begin regular image loading, adapted from mitkItkImageIO itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New(); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. nrrdImageIO->SetFileName(path); nrrdImageIO->ReadImageInformation(); unsigned int ndim = nrrdImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = nrrdImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = nrrdImageIO->GetDimensions(i); spacing[i] = nrrdImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = nrrdImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; nrrdImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()]; nrrdImageIO->Read(buffer); image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = nrrdImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary(); TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); // end regular image loading LabelSetImage::Pointer output = ConvertImageToLabelSetImage(image); // get labels and add them as properties to the image char keybuffer[256]; unsigned int numberOfLayers = GetIntByKey(dictionary, "layers"); std::string _xmlStr; mitk::Label::Pointer label; for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) { sprintf(keybuffer, "layer_%03u", layerIdx); int numberOfLabels = GetIntByKey(dictionary, keybuffer); mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) { tinyxml2::XMLDocument doc; sprintf(keybuffer, "label_%03u_%05d", layerIdx, labelIdx); _xmlStr = GetStringByKey(dictionary, keybuffer); doc.Parse(_xmlStr.c_str(), _xmlStr.size()); auto *labelElem = doc.FirstChildElement("Label"); if (labelElem == nullptr) mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; label = mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(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); if (loadedProp.IsNull()) { MITK_ERROR << "Property cannot be correctly deserialized and is skipped. Check if data format is valid. Problematic property value string: \"" << value << "\"; Property info used to deserialized: " << info; break; } output->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey = false; for (const auto& defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } // Handle UID if (dictionary.HasKey(PROPERTY_KEY_UID)) { itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); if (uidData.IsNotNull()) { mitk::UIDManipulator uidManipulator(output); uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); } } MITK_INFO << "...finished!"; std::vector result; result.push_back(output.GetPointer()); return result; } int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return atoi(metaString.c_str()); } } return 0; } std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return metaString; } } return metaString; } LabelSetImageIO *LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); } void LabelSetImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); this->m_DefaultMetaDataKeys.push_back("label_"); this->m_DefaultMetaDataKeys.push_back("layer_"); } } // namespace #endif //__mitkLabelSetImageWriter__cpp diff --git a/Modules/Multilabel/mitkLabelSetImageConverter.cpp b/Modules/Multilabel/mitkLabelSetImageConverter.cpp index 15868c5d6d..8486db5c76 100644 --- a/Modules/Multilabel/mitkLabelSetImageConverter.cpp +++ b/Modules/Multilabel/mitkLabelSetImageConverter.cpp @@ -1,145 +1,145 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include template static void ConvertLabelSetImageToImage(const itk::Image *, mitk::LabelSetImage::ConstPointer labelSetImage, mitk::Image::Pointer &image) { typedef itk::Image ImageType; typedef itk::ComposeImageFilter ComposeFilterType; typedef itk::ImageDuplicator DuplicatorType; auto numberOfLayers = labelSetImage->GetNumberOfLayers(); if (numberOfLayers > 1) { auto vectorImageComposer = ComposeFilterType::New(); auto activeLayer = labelSetImage->GetActiveLayer(); for (decltype(numberOfLayers) layer = 0; layer < numberOfLayers; ++layer) { auto layerImage = mitk::ImageToItkImage( layer != activeLayer ? labelSetImage->GetLayerImage(layer) : labelSetImage); vectorImageComposer->SetInput(layer, layerImage); } vectorImageComposer->Update(); // mitk::GrabItkImageMemory does not support 4D, this will handle 4D correctly // and create a memory managed copy image = mitk::ImportItkImage(vectorImageComposer->GetOutput())->Clone(); } else { auto layerImage = mitk::ImageToItkImage(labelSetImage); auto duplicator = DuplicatorType::New(); duplicator->SetInputImage(layerImage); duplicator->Update(); // mitk::GrabItkImageMemory does not support 4D, this will handle 4D correctly // and create a memory managed copy image = mitk::ImportItkImage(duplicator->GetOutput())->Clone(); } } mitk::Image::Pointer mitk::ConvertLabelSetImageToImage(LabelSetImage::ConstPointer labelSetImage) { Image::Pointer image; if (labelSetImage->GetNumberOfLayers() > 0) { if (labelSetImage->GetDimension() == 4) { AccessFixedDimensionByItk_n(labelSetImage, ::ConvertLabelSetImageToImage, 4, (labelSetImage, image)); } else { AccessByItk_2(labelSetImage->GetLayerImage(0), ::ConvertLabelSetImageToImage, labelSetImage, image); } image->SetTimeGeometry(labelSetImage->GetTimeGeometry()->Clone()); } return image; } template static void ConvertImageToLabelSetImage(const itk::VectorImage *image, mitk::LabelSetImage::Pointer &labelSetImage) { typedef itk::VectorImage VectorImageType; typedef itk::Image ImageType; typedef itk::VectorIndexSelectionCastImageFilter VectorIndexSelectorType; labelSetImage = mitk::LabelSetImage::New(); auto numberOfLayers = image->GetVectorLength(); for (decltype(numberOfLayers) layer = 0; layer < numberOfLayers; ++layer) { auto layerSelector = VectorIndexSelectorType::New(); layerSelector->SetInput(image); layerSelector->SetIndex(layer); layerSelector->Update(); mitk::Image::Pointer layerImage; mitk::CastToMitkImage(layerSelector->GetOutput(), layerImage); if (layer == 0) { labelSetImage->InitializeByLabeledImage(layerImage); } else { labelSetImage->AddLayer(layerImage); } } } mitk::LabelSetImage::Pointer mitk::ConvertImageToLabelSetImage(Image::Pointer image) { LabelSetImage::Pointer labelSetImage; if (image.IsNotNull()) { - if (image->GetChannelDescriptor().GetPixelType().GetPixelType() == itk::ImageIOBase::VECTOR) + if (image->GetChannelDescriptor().GetPixelType().GetPixelType() == itk::IOPixelEnum::VECTOR) { if (4 == image->GetDimension()) { AccessVectorFixedDimensionByItk_n(image, ::ConvertImageToLabelSetImage, 4, (labelSetImage)); } else { AccessVectorPixelTypeByItk_n(image, ::ConvertImageToLabelSetImage, (labelSetImage)); } } else { labelSetImage = mitk::LabelSetImage::New(); labelSetImage->InitializeByLabeledImage(image); } labelSetImage->SetTimeGeometry(image->GetTimeGeometry()->Clone()); } return labelSetImage; } diff --git a/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp b/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp index 356659fe52..294c802013 100644 --- a/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp +++ b/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.cpp @@ -1,412 +1,391 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 headers #include "mitkGrabCutOpenCVImageFilter.h" #include "mitkPointSet.h" -// itk headers -#include "itkMultiThreader.h" -#include "itkFastMutexLock.h" -#include "itkConditionVariable.h" - #include // This is a magic number defined in "grabcut.cpp" of OpenCV. // GrabCut function crashes if less than this number of model // points are given. There must be at least as much model points // as components of the Gaussian Mixture Model. #define GMM_COMPONENTS_COUNT 5 mitk::GrabCutOpenCVImageFilter::GrabCutOpenCVImageFilter() : m_ModelPointsDilationSize(0), m_UseOnlyRegionAroundModelPoints(false), m_CurrentProcessImageNum(0), m_InputImageId(AbstractOpenCVImageFilter::INVALID_IMAGE_ID), m_ResultImageId(AbstractOpenCVImageFilter::INVALID_IMAGE_ID), - m_ThreadId(-1), m_StopThread(false), - m_MultiThreader(itk::MultiThreader::New()), - m_WorkerBarrier(itk::ConditionVariable::New()), - m_ImageMutex(itk::FastMutexLock::New()), - m_ResultMutex(itk::FastMutexLock::New()), - m_PointSetsMutex(itk::FastMutexLock::New()) + m_StopThread(false) { - m_ThreadId = m_MultiThreader->SpawnThread(this->SegmentationWorker, this); + m_Thread = std::thread(&GrabCutOpenCVImageFilter::SegmentationWorker, this); } mitk::GrabCutOpenCVImageFilter::~GrabCutOpenCVImageFilter() { // terminate worker thread on destruction m_StopThread = true; - m_WorkerBarrier->Broadcast(); - if ( m_ThreadId >= 0) { m_MultiThreader->TerminateThread(m_ThreadId); } + m_WorkerBarrier.notify_all(); + if (m_Thread.joinable()) + m_Thread.detach(); } bool mitk::GrabCutOpenCVImageFilter::OnFilterImage( cv::Mat& image ) { if ( image.empty() ) { MITK_WARN << "Filtering empty image?"; return false; } // make sure that the image is an rgb image as needed // by the GrabCut algorithm if (image.type() != CV_8UC3) { cv::Mat tmp = image.clone(); cv::cvtColor(tmp, image, CV_GRAY2RGB); } // set image as the current input image, guarded by // a mutex as the worker thread reads this imagr - m_ImageMutex->Lock(); + m_ImageMutex.lock(); m_InputImage = image.clone(); m_InputImageId = this->GetCurrentImageId(); - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); // wake up the worker thread if there was an image set // and foreground model points are available - if ( ! m_ForegroundPoints.empty()) { m_WorkerBarrier->Broadcast(); } + if ( ! m_ForegroundPoints.empty()) { m_WorkerBarrier.notify_all(); } return true; } void mitk::GrabCutOpenCVImageFilter::SetModelPoints(ModelPointsList foregroundPoints) { - m_PointSetsMutex->Lock(); + m_PointSetsMutex.lock(); m_ForegroundPoints = foregroundPoints; - m_PointSetsMutex->Unlock(); + m_PointSetsMutex.unlock(); } void mitk::GrabCutOpenCVImageFilter::SetModelPoints(ModelPointsList foregroundPoints, ModelPointsList backgroundPoints) { - m_PointSetsMutex->Lock(); + m_PointSetsMutex.lock(); m_BackgroundPoints = backgroundPoints; m_ForegroundPoints = foregroundPoints; - m_PointSetsMutex->Unlock(); + m_PointSetsMutex.unlock(); } void mitk::GrabCutOpenCVImageFilter::SetModelPoints(cv::Mat foregroundMask) { - m_PointSetsMutex->Lock(); + m_PointSetsMutex.lock(); m_ForegroundPoints = this->ConvertMaskToModelPointsList(foregroundMask); - m_PointSetsMutex->Unlock(); + m_PointSetsMutex.unlock(); } void mitk::GrabCutOpenCVImageFilter::SetModelPoints(cv::Mat foregroundMask, cv::Mat backgroundMask) { - m_PointSetsMutex->Lock(); + m_PointSetsMutex.lock(); m_ForegroundPoints = this->ConvertMaskToModelPointsList(foregroundMask); m_BackgroundPoints = this->ConvertMaskToModelPointsList(backgroundMask); - m_PointSetsMutex->Unlock(); + m_PointSetsMutex.unlock(); } void mitk::GrabCutOpenCVImageFilter::SetModelPointsDilationSize(int modelPointsDilationSize) { if ( modelPointsDilationSize < 0 ) { MITK_ERROR("AbstractOpenCVImageFilter")("GrabCutOpenCVImageFilter") << "Model points dilation size must not be smaller then zero."; mitkThrow() << "Model points dilation size must not be smaller then zero."; } m_ModelPointsDilationSize = modelPointsDilationSize; } void mitk::GrabCutOpenCVImageFilter::SetUseOnlyRegionAroundModelPoints(unsigned int additionalWidth) { m_UseOnlyRegionAroundModelPoints = true; m_AdditionalWidth = additionalWidth; } void mitk::GrabCutOpenCVImageFilter::SetUseFullImage() { m_UseOnlyRegionAroundModelPoints = false; } cv::Rect mitk::GrabCutOpenCVImageFilter::GetRegionAroundModelPoints() { return m_BoundingBox; } int mitk::GrabCutOpenCVImageFilter::GetResultImageId() { return m_ResultImageId; } cv::Mat mitk::GrabCutOpenCVImageFilter::GetResultMask() { cv::Mat result; - m_ResultMutex->Lock(); + m_ResultMutex.lock(); result = m_ResultMask.clone(); - m_ResultMutex->Unlock(); + m_ResultMutex.unlock(); return result; } std::vector mitk::GrabCutOpenCVImageFilter::GetResultContours() { std::vector > cvContours; std::vector hierarchy; std::vector contourPoints; cv::Mat resultMask = this->GetResultMask(); if (resultMask.empty()) { return contourPoints; } cv::findContours(resultMask, cvContours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE); // convert cvContours to vector of ModelPointsLists for ( unsigned int i = 0; i < cvContours.size(); ++i ) { mitk::GrabCutOpenCVImageFilter::ModelPointsList curContourPoints; for ( auto it = cvContours[i].begin(); it != cvContours[i].end(); ++it) { itk::Index<2> index; index.SetElement(0, it->x); index.SetElement(1, it->y); curContourPoints.push_back(index); } contourPoints.push_back(curContourPoints); } return contourPoints; } mitk::GrabCutOpenCVImageFilter::ModelPointsList mitk::GrabCutOpenCVImageFilter::GetResultContourWithPixel(itk::Index<2> pixelIndex) { cv::Mat mask = this->GetResultMask(); if (mask.empty()) { return mitk::GrabCutOpenCVImageFilter::ModelPointsList(); } // return empty model point list if given pixel is outside the image borders if (pixelIndex.GetElement(0) < 0 || pixelIndex.GetElement(0) >= mask.size().height || pixelIndex.GetElement(1) < 0 || pixelIndex.GetElement(1) >= mask.size().width) { MITK_WARN("AbstractOpenCVImageFilter")("GrabCutOpenCVImageFilter") << "Given pixel index ("<< pixelIndex.GetElement(0) << ", " << pixelIndex.GetElement(1) << ") is outside the image (" << mask.size().height << ", " << mask.size().width << ")."; return mitk::GrabCutOpenCVImageFilter::ModelPointsList(); } // create a mask where the segmentation around the given pixel index is // set (done by flood filling the result mask using the pixel as seed) cv::floodFill(mask, cv::Point(pixelIndex.GetElement(0), pixelIndex.GetElement(1)), 5); cv::Mat foregroundMask; cv::compare(mask, 5, foregroundMask, cv::CMP_EQ); // find the contour on the flood filled image (there can be only one now) std::vector > cvContours; std::vector hierarchy; cv::findContours(foregroundMask, cvContours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE); ModelPointsList contourPoints; // convert cvContours to ModelPointsList for ( auto it = cvContours[0].begin(); it != cvContours[0].end(); ++it) { itk::Index<2> index; index.SetElement(0, it->x); index.SetElement(1, it->y); contourPoints.push_back(index); } return contourPoints; } cv::Mat mitk::GrabCutOpenCVImageFilter::GetMaskFromPointSets() { // initialize mask with values of propably background cv::Mat mask(m_InputImage.size().height, m_InputImage.size().width, CV_8UC1, cv::GC_PR_BGD); // get foreground and background points (guarded by mutex) - m_PointSetsMutex->Lock(); + m_PointSetsMutex.lock(); ModelPointsList pointsLists[2] = {ModelPointsList(m_ForegroundPoints), ModelPointsList(m_BackgroundPoints)}; - m_PointSetsMutex->Unlock(); + m_PointSetsMutex.unlock(); // define values for foreground and background pixels unsigned int pixelValues[2] = {cv::GC_FGD, cv::GC_BGD}; for (unsigned int n = 0; n < 2; ++n) { for (auto it = pointsLists[n].begin(); it != pointsLists[n].end(); ++it) { // set pixels around current pixel to the same value (size of this // area is specified by ModelPointsDilationSize) for ( int i = -m_ModelPointsDilationSize; i <= m_ModelPointsDilationSize; ++i ) { for ( int j = -m_ModelPointsDilationSize; j <= m_ModelPointsDilationSize; ++j) { int x = it->GetElement(1) + i; int y = it->GetElement(0) + j; if ( x >= 0 && y >= 0 && x < mask.cols && y < mask.rows) { mask.at(x, y) = pixelValues[n]; } } } } } return mask; } cv::Rect mitk::GrabCutOpenCVImageFilter::GetBoundingRectFromMask(cv::Mat mask) { cv::Mat nonPropablyBackgroundMask, modelPoints; cv::compare(mask, cv::GC_PR_BGD, nonPropablyBackgroundMask, cv::CMP_NE); cv::findNonZero(nonPropablyBackgroundMask, modelPoints); if (modelPoints.empty()) { MITK_WARN("AbstractOpenCVImageFilter")("GrabCutOpenCVImageFilter") << "Cannot find any foreground points. Returning full image size as bounding rectangle."; return cv::Rect(0, 0, mask.rows, mask.cols); } // calculate bounding rect around the model points cv::Rect boundingRect = cv::boundingRect(modelPoints); // substract additional width to x and y value (and make sure that they aren't outside the image then) boundingRect.x = static_cast(boundingRect.x) > m_AdditionalWidth ? boundingRect.x - m_AdditionalWidth : 0; boundingRect.y = static_cast(boundingRect.y) > m_AdditionalWidth ? boundingRect.y - m_AdditionalWidth : 0; // add additional width to width of bounding rect (twice as x value was moved before) // and make sure that the bounding rect will stay inside the image borders) if ( static_cast(boundingRect.x + boundingRect.width) + 2 * m_AdditionalWidth < static_cast(mask.size().width) ) { boundingRect.width += 2 * m_AdditionalWidth; } else { boundingRect.width = mask.size().width - boundingRect.x - 1; } // add additional width to height of bounding rect (twice as y value was moved before) // and make sure that the bounding rect will stay inside the image borders) if ( static_cast(boundingRect.y + boundingRect.height) + 2 * m_AdditionalWidth < static_cast(mask.size().height) ) { boundingRect.height += 2 * m_AdditionalWidth; } else { boundingRect.height = mask.size().height - boundingRect.y - 1; } assert(boundingRect.x + boundingRect.width < mask.size().width); assert(boundingRect.y + boundingRect.height < mask.size().height); return boundingRect; } cv::Mat mitk::GrabCutOpenCVImageFilter::RunSegmentation(cv::Mat input, cv::Mat mask) { // test if foreground and background models are large enough for GrabCut cv::Mat compareFgResult, compareBgResult; cv::compare(mask, cv::GC_FGD, compareFgResult, cv::CMP_EQ); cv::compare(mask, cv::GC_PR_BGD, compareBgResult, cv::CMP_EQ); if ( cv::countNonZero(compareFgResult) < GMM_COMPONENTS_COUNT || cv::countNonZero(compareBgResult) < GMM_COMPONENTS_COUNT) { // return result mask with no pixels set to foreground return cv::Mat::zeros(mask.size(), mask.type()); } // do the actual grab cut segmentation (initialized with the mask) cv::Mat bgdModel, fgdModel; cv::grabCut(input, mask, cv::Rect(), bgdModel, fgdModel, 1, cv::GC_INIT_WITH_MASK); // set propably foreground pixels to white on result mask cv::Mat result; cv::compare(mask, cv::GC_PR_FGD, result, cv::CMP_EQ); // set foreground pixels to white on result mask cv::Mat foregroundMat; cv::compare(mask, cv::GC_FGD, foregroundMat, cv::CMP_EQ); foregroundMat.copyTo(result, foregroundMat); return result; // now the result mask can be returned } mitk::GrabCutOpenCVImageFilter::ModelPointsList mitk::GrabCutOpenCVImageFilter::ConvertMaskToModelPointsList(cv::Mat mask) { cv::Mat points; cv::findNonZero(mask, points); // push extracted points into a vector of itk indices ModelPointsList pointsVector; for ( size_t n = 0; n < points.total(); ++n) { itk::Index<2> index; index.SetElement(0, points.at(n).x); index.SetElement(1, points.at(n).y); pointsVector.push_back(index); } return pointsVector; } -ITK_THREAD_RETURN_TYPE mitk::GrabCutOpenCVImageFilter::SegmentationWorker(void* pInfoStruct) +void mitk::GrabCutOpenCVImageFilter::SegmentationWorker() { - // extract this pointer from thread info structure - struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - mitk::GrabCutOpenCVImageFilter* thisObject = static_cast(pInfo->UserData); - - itk::SimpleMutexLock mutex; - mutex.Lock(); + std::mutex mutex; + std::unique_lock lock(mutex); while (true) { - if (thisObject->m_StopThread) { break; } - - thisObject->m_WorkerBarrier->Wait(&mutex); + m_WorkerBarrier.wait(lock, [this] { return !m_StopThread; }); - if (thisObject->m_StopThread) { break; } + m_ImageMutex.lock(); + cv::Mat image = m_InputImage.clone(); + int inputImageId = m_InputImageId; + m_ImageMutex.unlock(); - thisObject->m_ImageMutex->Lock(); - cv::Mat image = thisObject->m_InputImage.clone(); - int inputImageId = thisObject->m_InputImageId; - thisObject->m_ImageMutex->Unlock(); - - cv::Mat mask = thisObject->GetMaskFromPointSets(); + cv::Mat mask = this->GetMaskFromPointSets(); cv::Mat result; - if (thisObject->m_UseOnlyRegionAroundModelPoints) + if (m_UseOnlyRegionAroundModelPoints) { result = cv::Mat(mask.rows, mask.cols, mask.type(), 0.0); - thisObject->m_BoundingBox = thisObject->GetBoundingRectFromMask(mask); - thisObject->RunSegmentation(image(thisObject->m_BoundingBox), mask(thisObject->m_BoundingBox)).copyTo(result(thisObject->m_BoundingBox)); + m_BoundingBox = this->GetBoundingRectFromMask(mask); + RunSegmentation(image(m_BoundingBox), mask(m_BoundingBox)).copyTo(result(m_BoundingBox)); } else { - result = thisObject->RunSegmentation(image, mask); + result = this->RunSegmentation(image, mask); } // save result to member attribute - thisObject->m_ResultMutex->Lock(); - thisObject->m_ResultMask = result; - thisObject->m_ResultImageId = inputImageId; - thisObject->m_ResultMutex->Unlock(); + m_ResultMutex.lock(); + m_ResultMask = result; + m_ResultImageId = inputImageId; + m_ResultMutex.unlock(); } - - mutex.Unlock(); - - return ITK_THREAD_RETURN_VALUE; } diff --git a/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.h b/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.h index 361c0dad68..9cd859477a 100644 --- a/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.h +++ b/Modules/OpenCVVideoSupport/Commands/mitkGrabCutOpenCVImageFilter.h @@ -1,287 +1,289 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKGRABCUTOPENCVIMAGEFILTER_H #define MITKGRABCUTOPENCVIMAGEFILTER_H // mitk headers #include "mitkAbstractOpenCVImageFilter.h" #include "mitkVector.h" // itk headers #include "itkObjectFactory.h" -#include "itkMutexLock.h" +#include // opencv headers #include +#include +#include +#include + namespace itk { template class Index; template class SmartPointer; class MultiThreader; class ConditionVariable; class FastMutexLock; } namespace mitk { class PointSet; /** * \brief Makes the OpenCV GrabCut filter available as OpenCVImageFilter. * * Image filtering is done asynchronly by using a worker thread as GrabCut segmentation * can take up to some seconds. Calling the mitk::GrabCutOpenCVImageFilter::OnFilterImage() * method sets just the input image and wakes up the worker thread. It is not guaranteed * that every image gets segmented. If multiple new images where set before a segmentation * was finished, only the last new image gets segmented afterwards. * * At least foreground model points have to be set by * mitk::GrabCutOpenCVImageFilter::SetModelPoints() before a segmentation can be performed. * The worder thread will not be waken up before any model points were set. * * When a new segmentation is ready, mitk::GrabCutOpenCVImageFilter::GetCurrentImageId() * returns a new image id. The segmentation can be got then by calling * mitk::GrabCutOpenCVImageFilter::GetResultMask(), * mitk::GrabCutOpenCVImageFilter::GetResultContours() or * mitk::GrabCutOpenCVImageFilter::GetResultContourWithPixel(). */ class MITKOPENCVVIDEOSUPPORT_EXPORT GrabCutOpenCVImageFilter : public AbstractOpenCVImageFilter { public: /** \brief List holding image indices of the model points. */ typedef std::vector > ModelPointsList; mitkClassMacro(GrabCutOpenCVImageFilter, AbstractOpenCVImageFilter); itkFactorylessNewMacro(Self); itkCloneMacro(Self); GrabCutOpenCVImageFilter(); ~GrabCutOpenCVImageFilter() override; /** * \brief Implementation of the virtual image filtering method. * The input image is copied to a member attribute, but the actual filtering is not done * in this method. Instead a worker thread is waken up every time this method is called, * if foreground model points were set before. * * \param image OpenCV image to be segmentated * \return false if an empty image was set, true otherwise */ bool OnFilterImage( cv::Mat& image ) override; /** * \brief Sets a list of image indices as foreground model points. * \param foregroundPoints List of image indices which definitely belong to the foreground. */ void SetModelPoints(ModelPointsList foregroundPoints); /** * \brief Sets a list of image indices as foreground and another list as background model points. * \param foregroundPoints List of image indices which definitely belong to the foreground. * \param backgroundPoints List of image indices which definitely belong to the background. */ void SetModelPoints(ModelPointsList foregroundPoints, ModelPointsList backgroundPoints); /** * \brief Sets a mask where every non-zero pixel is treated as a foreground model pixel. */ void SetModelPoints(cv::Mat foregroundMask); /** * \brief Sets masks specifying foreground and background points. * \param foregroundMask every non-zero pixel is treated as a foreground model pixel * \param backgroundMask every non-zero pixel is treated as a background model pixel */ void SetModelPoints(cv::Mat foregroundMask, cv::Mat backgroundMask); /** * \brief Set a size of which each model point is dilated before image filtering. * The more color information of the foreground object the GrabCut filter gets the better * the result will be. Therefore the foreground pixels can be dilated before filtering. The * caller is responsible for setting a dilation size so that no foreground model pixels will * be indeed part of the background after dilation. * * Dilation is done to the background model pixles as well, if there are any set for the * filter. * * \param modelPointsDilationSize how many pixels are added in every direction, 0 sets back to no dilation */ void SetModelPointsDilationSize(int modelPointsDilationSize); /** * \brief Use only the region around the foreground model points for the segmentation. * * This is mainly for reasons of segmentation speed and has the drawback that the foreground * model points (plus the given additional border) have to cover the whole foreground object. * * The segmentation filter can be set back to using the whole image by calling * mitk::GrabCutOpenCVImageFilter::SetUseFullImage(). * * \param additionalBorder size of the border around the foreground points which will be used for segmentation, too */ void SetUseOnlyRegionAroundModelPoints(unsigned int additionalBorder); /** * \brief The full image is used as input for the segmentation. * This method sets the behaviour back to the default behaviour in case * mitk::GrabCutOpenCVImageFilter::SetUseOnlyRegionAroundModelPoints() was * called before. */ void SetUseFullImage(); /** * \brief Getter for the rectangle used for the area of segmentation. * See mitk::GrabCutOpenCVImageFilter::SetUseOnlyRegionAroundModelPoints(). * This method is mainly for debugging purposes and may be removed in * the future. */ cv::Rect GetRegionAroundModelPoints(); /** * \brief Getter for an ascending id of the current result image. * The id will be increased for every segmentation that is produced by the worker thread. * It can be used to determine if a new segmentation was produced since the last time a * segmentation was got from this filter. * * int lastResultImageId = grabCutFilter->GetResultImageId(); * // do something * if ( lastResultImageId != grabCutFilter->GetResultImageId() ) * // get new segmentation */ int GetResultImageId(); /** * \brief Getter for the result mask of the current segmentation. * The result of this method is not necessarily consistent with the result of * mitk::GrabCutOpenCVImageFilter::GetResultContours() if they are called afterwards. * The segmentation may have changed in the meantime. One should decide if he needs * a mask or a contour or convert one into the other on his own. * \return image of the size of the input image where all pixels segmented as foreground are non-zero */ cv::Mat GetResultMask(); /** * \brief Getter for the contours of the current segmentation. * * A segmentation can consist of multiple regions therefore a list of contours * is returned. If one needs only one specific region he can call * mitk::GrabCutOpenCVImageFilter::GetResultContourWithPixel(). * * This result of this method is not necessarily consistent with the result of * mitk::GrabCutOpenCVImageFilter::GetResultContours() if they are called afterwards. * The segmentation may have changed in the meantime. One should decide if he needs * a mask or a contour or convert one into the other on his own. * * \return List containing lists of pixel indices for every contour. */ std::vector GetResultContours(); /** * \brief Getter for one specific contour of the current segmentation. * * Can be used if only one (of possible multiple contours) is needed. A pixel index * must be given to select from the contours. This could be one of the foreground * model pixels for example. If other criteria are needed to distinguish the contours * mitk::GrabCutOpenCVImageFilter::GetResultContours() can be used instead and therefore * contour selection can be done by hand then. * * This result of this method is not necessarily consistent with the result of * mitk::GrabCutOpenCVImageFilter::GetResultContours() if they are called afterwards. * The segmentation may have changed in the meantime. One should decide if he needs * a mask or a contour or convert one into the other on his own. * * \param pixelIndex index of a pixel which lies inside the contour * \return list of pixel indices for the selected contour */ ModelPointsList GetResultContourWithPixel(itk::Index<2> pixelIndex); protected: /** \brief Creates an image mask for GrabCut algorithm by using the foreground and background point sets. * Background and foreground points will be dilated by the size set by * mitk::GrabCutOpenCVImageFilter::SetModelPointsDilationSize(). */ cv::Mat GetMaskFromPointSets(); /** * \brief Creates a bounding box around all pixels which aren't propably background. * The bounding box is widened as specified by * mitk::GrabCutOpenCVImageFilter::SetUseOnlyRegionAroundModelPoints(). */ cv::Rect GetBoundingRectFromMask(cv::Mat mask); /** * \brief Performs a GrabCut segmentation of the given input image. * \param input image on which the segmentation will be performed * \param mask foreground and background pixels used as basis for segmentation * \return mask with every pixel of the segmented foreground object set non-zero */ cv::Mat RunSegmentation(cv::Mat input, cv::Mat mask); /** * \brief Creates a list of points from every non-zero pixel of the given mask. */ ModelPointsList ConvertMaskToModelPointsList(cv::Mat mask); int m_ModelPointsDilationSize; bool m_UseOnlyRegionAroundModelPoints; unsigned int m_AdditionalWidth; cv::Rect m_BoundingBox; ModelPointsList m_ForegroundPoints; ModelPointsList m_BackgroundPoints; cv::Mat m_InputImage; cv::Mat m_GrabCutMask; cv::Mat m_ResultMask; unsigned int m_CurrentProcessImageNum; /** \brief id of the image currently set as m_InputImage */ int m_InputImageId; /** \brief id of the image which segmentation result is currently present in m_ResultMask */ int m_ResultImageId; private: /** * \brief Worker thread for doing the segmentation. * It blocks every time a image was segmented and will be waken up again by * the mitk::GrabCutOpenCVImageFilter::OnFilterImage() method. * - * \param pInfoStruct pointer to the GrabCutOpenCVImageFilter object * \return */ - static ITK_THREAD_RETURN_TYPE SegmentationWorker(void* pInfoStruct); + void SegmentationWorker(); - int m_ThreadId; + std::thread m_Thread; /** \brief worker thread will terminate after the next wakeup if set to true */ bool m_StopThread; - itk::SmartPointer m_MultiThreader; - itk::SmartPointer m_WorkerBarrier; + std::condition_variable m_WorkerBarrier; /** \brief mutex for guarding m_InputImage and m_InputImageId */ - itk::SmartPointer m_ImageMutex; + std::mutex m_ImageMutex; /** \brief mutex for guarding m_ResultMask and m_ResultImageId */ - itk::SmartPointer m_ResultMutex; + std::mutex m_ResultMutex; /** \brief mutex for guarding m_ForegroundPoints and m_BackgroundPoints */ - itk::SmartPointer m_PointSetsMutex; + std::mutex m_PointSetsMutex; }; } // namespace mitk #endif // MITKGRABCUTOPENCVIMAGEFILTER_H diff --git a/Modules/OpenCVVideoSupport/Testing/mitkOpenCVToMitkImageFilterTest.cpp b/Modules/OpenCVVideoSupport/Testing/mitkOpenCVToMitkImageFilterTest.cpp index 9ce7fa7591..7bab414242 100644 --- a/Modules/OpenCVVideoSupport/Testing/mitkOpenCVToMitkImageFilterTest.cpp +++ b/Modules/OpenCVVideoSupport/Testing/mitkOpenCVToMitkImageFilterTest.cpp @@ -1,231 +1,202 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 includes #include "mitkOpenCVToMitkImageFilter.h" #include #include #include -#include #include #include #include #include +#include /** Documentation * * @brief Objects of this class can start an internal thread by calling the Start() method. * The thread is then updateing the tested object until the method Stop() is called. The class * can be used to test if a filter is thread-save by using multiple objects and let * them update simuntanously. */ class mitkTestOpenCVToMITKImageFilterThread : public itk::Object { public: mitkClassMacroItkParent(mitkTestOpenCVToMITKImageFilterThread, itk::Object); - mitkNewMacro1Param(mitkTestOpenCVToMITKImageFilterThread, itk::MultiThreader::Pointer); + itkNewMacro(mitkTestOpenCVToMITKImageFilterThread); int NumberOfMessages; protected: - mitkTestOpenCVToMITKImageFilterThread(itk::MultiThreader::Pointer MultiThreader) + mitkTestOpenCVToMITKImageFilterThread() { - ThreadID = -1; NumberOfMessages = 0; - m_MultiThreader = MultiThreader; - } bool ThreadRunning; - int ThreadID; + std::thread Thread; cv::Mat currentImage; mitk::OpenCVToMitkImageFilter::Pointer m_testedFilter; - itk::MultiThreader::Pointer m_MultiThreader; - void DoSomething() { while (ThreadRunning) { m_testedFilter->SetOpenCVMat(currentImage); m_testedFilter->Update(); mitk::Image::Pointer result; result = m_testedFilter->GetOutput(); - //std::cout << "Thread " << ThreadID << " Update Call" << std::endl; } } - static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* pInfoStruct) + void ThreadStartTracking() { - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - if (pInfo == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - mitkTestOpenCVToMITKImageFilterThread *thisthread = (mitkTestOpenCVToMITKImageFilterThread*)pInfo->UserData; - - if (thisthread != nullptr) - thisthread->DoSomething(); - - return ITK_THREAD_RETURN_VALUE; + this->DoSomething(); } public: - int Start() + void Start() { ThreadRunning = true; - this->ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); - return ThreadID; + this->Thread = std::thread(&mitkTestOpenCVToMITKImageFilterThread::ThreadStartTracking, this); } void Stop() { ThreadRunning = false; + if (this->Thread.joinable()) + this->Thread.join(); } void setFilter(mitk::OpenCVToMitkImageFilter::Pointer testedFilter) { m_testedFilter = testedFilter; } void setImage(cv::Mat image) { currentImage = image; } }; class mitkOpenCVToMitkImageFilterTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkOpenCVToMitkImageFilterTestSuite); MITK_TEST(TestInitialization); MITK_TEST(TestThreadSafety); CPPUNIT_TEST_SUITE_END(); private: cv::Mat image1,image2,image3,image4,image5; mitk::OpenCVToMitkImageFilter::Pointer testFilter; public: void setUp() override { image1 = cv::imread(GetTestDataFilePath("NrrdWritingTestImage.jpg").c_str()); image2 = cv::imread(GetTestDataFilePath("Png2D-bw.png").c_str()); image3 = cv::imread(GetTestDataFilePath("OpenCV-Data/CroppedImage.png").c_str()); image4 = cv::imread(GetTestDataFilePath("OpenCV-Data/GrabCutMask.png").c_str()); image5 = cv::imread(GetTestDataFilePath("OpenCV-Data/GrabCutOutput.png").c_str()); testFilter = mitk::OpenCVToMitkImageFilter::New(); //change input testFilter->SetOpenCVMat(image1); } void tearDown() override { } void TestInitialization() { testFilter = mitk::OpenCVToMitkImageFilter::New(); MITK_TEST_OUTPUT(<<"Testing Initialization"); } void TestThreadSafety() { - std::vector threadIDs; - std::vector threads; - itk::MultiThreader::Pointer multiThreader = itk::MultiThreader::New(); MITK_TEST_OUTPUT(<< "Testing Thread Safety with 2 Threads"); //create two threads - mitkTestOpenCVToMITKImageFilterThread::Pointer newThread1 = mitkTestOpenCVToMITKImageFilterThread::New(multiThreader); + auto newThread1 = mitkTestOpenCVToMITKImageFilterThread::New(); newThread1->setFilter(testFilter); newThread1->setImage(image1); - threads.push_back(newThread1); - mitkTestOpenCVToMITKImageFilterThread::Pointer newThread2 = mitkTestOpenCVToMITKImageFilterThread::New(multiThreader); + auto newThread2 = mitkTestOpenCVToMITKImageFilterThread::New(); newThread2->setFilter(testFilter); newThread2->setImage(image1); - threads.push_back(newThread2); - //start both - unsigned int id1 = newThread1->Start(); - unsigned int id2 = newThread2->Start(); + newThread1->Start(); + newThread2->Start(); int delay = 1; - for (int i = 0; i < 10000; i++) + for (int i = 0; i < 100; i++) { //std::cout << "Run " << i << std::endl; //wait a bit itksys::SystemTools::Delay(delay); //change input newThread1->setImage(image2); newThread1->setImage(image3); //wait a bit itksys::SystemTools::Delay(delay); //change input newThread1->setImage(image4); newThread1->setImage(image5); //wait a bit itksys::SystemTools::Delay(delay); //change input newThread1->setImage(image2); newThread1->setImage(image2); //wait a bit itksys::SystemTools::Delay(delay); //change input newThread1->setImage(image3); newThread1->setImage(image3); //wait a bit itksys::SystemTools::Delay(delay); } //stop both threads newThread1->Stop(); newThread2->Stop(); - multiThreader->TerminateThread(id1); - multiThreader->TerminateThread(id2); - MITK_TEST_OUTPUT(<< "Testing Thread Safety with 2 Threads"); } private: }; MITK_TEST_SUITE_REGISTRATION(mitkOpenCVToMitkImageFilter) diff --git a/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.cpp b/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.cpp index fcffa8b612..ab427e98e9 100644 --- a/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.cpp +++ b/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.cpp @@ -1,155 +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 "mitkOpenCVToMitkImageFilter.h" #include #include #include #include #include #include "mitkImageToOpenCVImageFilter.h" namespace mitk{ OpenCVToMitkImageFilter::OpenCVToMitkImageFilter() { - m_ImageMutex = itk::FastMutexLock::New(); - m_OpenCVMatMutex = itk::FastMutexLock::New(); } OpenCVToMitkImageFilter::~OpenCVToMitkImageFilter() { } void OpenCVToMitkImageFilter::SetOpenCVMat(const cv::Mat &image) { - m_OpenCVMatMutex->Lock(); + m_OpenCVMatMutex.lock(); m_OpenCVMat = image; - m_OpenCVMatMutex->Unlock(); + m_OpenCVMatMutex.unlock(); this->Modified(); } void OpenCVToMitkImageFilter::SetOpenCVImage(const IplImage* image) { const cv::Mat cvMat = cv::cvarrToMat(image, false); this->SetOpenCVMat(cvMat); } void OpenCVToMitkImageFilter::GenerateData() { if (m_OpenCVMat.cols != 0 && m_OpenCVMat.rows != 0 && m_OpenCVMat.data) { // copy current cvMat - m_OpenCVMatMutex->Lock(); + m_OpenCVMatMutex.lock(); const cv::Mat input = m_OpenCVMat; - m_OpenCVMatMutex->Unlock(); + m_OpenCVMatMutex.unlock(); // convert cvMat to mitk::Image - m_ImageMutex->Lock(); + m_ImageMutex.lock(); // now convert rgb image if ((input.depth() >= 0) && ((unsigned int)input.depth() == CV_8S) && (input.channels() == 1)) { m_Image = ConvertCVMatToMitkImage< char, 2>(input); } else if (input.depth() == CV_8U && input.channels() == 1) { m_Image = ConvertCVMatToMitkImage< unsigned char, 2>(input); } else if (input.depth() == CV_8U && input.channels() == 3) { m_Image = ConvertCVMatToMitkImage< UCRGBPixelType, 2>(input); } else if (input.depth() == CV_16U && input.channels() == 1) { m_Image = ConvertCVMatToMitkImage< unsigned short, 2>(input); } else if (input.depth() == CV_16U && input.channels() == 3) { m_Image = ConvertCVMatToMitkImage< USRGBPixelType, 2>(input); } else if (input.depth() == CV_32F && input.channels() == 1) { m_Image = ConvertCVMatToMitkImage< float, 2>(input); } else if (input.depth() == CV_32F && input.channels() == 3) { m_Image = ConvertCVMatToMitkImage< FloatRGBPixelType, 2>(input); } else if (input.depth() == CV_64F && input.channels() == 1) { m_Image = ConvertCVMatToMitkImage< double, 2>(input); } else if (input.depth() == CV_64F && input.channels() == 3) { m_Image = ConvertCVMatToMitkImage< DoubleRGBPixelType, 2>(input); } else { MITK_WARN << "Unknown image depth and/or pixel type. Cannot convert OpenCV to MITK image."; return; } - //inputMutex->Unlock(); - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); } else { MITK_WARN << "Cannot start filter. OpenCV Image not set."; return; } } ImageSource::OutputImageType* OpenCVToMitkImageFilter::GetOutput() { return m_Image; } /******************************************** * Converting from OpenCV image to ITK Image *********************************************/ template Image::Pointer mitk::OpenCVToMitkImageFilter::ConvertCVMatToMitkImage(const cv::Mat input) { typedef itk::Image< TPixel, VImageDimension > ImageType; typename ImageType::Pointer output = itk::OpenCVImageBridge::CVMatToITKImage(input); Image::Pointer mitkImage = Image::New(); mitkImage = GrabItkImageMemory(output); return mitkImage; } void OpenCVToMitkImageFilter::InsertOpenCVImageAsMitkTimeSlice(cv::Mat openCVImage, Image::Pointer mitkImage, int timeStep) { // convert it to an mitk::Image this->SetOpenCVMat(openCVImage); this->Modified(); this->Update(); //insert it as a timeSlice mitkImage->GetGeometry(timeStep)->SetSpacing(this->GetOutput()->GetGeometry()->GetSpacing()); mitkImage->GetGeometry(timeStep)->SetOrigin(this->GetOutput()->GetGeometry()->GetOrigin()); mitkImage->GetGeometry(timeStep)->SetIndexToWorldTransform(this->GetOutput()->GetGeometry()->GetIndexToWorldTransform()); mitk::ImageReadAccessor readAccess(this->GetOutput()); mitkImage->SetImportVolume(readAccess.GetData(), timeStep); mitkImage->Modified(); mitkImage->Update(); - m_ImageMutex->Lock(); + m_ImageMutex.lock(); m_Image = mitkImage; - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); } } // end namespace mitk diff --git a/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h b/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h index 283f91bc90..c92612cadd 100644 --- a/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h +++ b/Modules/OpenCVVideoSupport/mitkOpenCVToMitkImageFilter.h @@ -1,95 +1,96 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitkOpenCVToMitkImageFilter_h #define mitkOpenCVToMitkImageFilter_h // mitk includes #include #include #include // itk includes #include -#include #include // OpenCV includes #include +#include + namespace mitk { /// /// \brief Filter for creating MITK RGB Images from an OpenCV image /// class MITKOPENCVVIDEOSUPPORT_EXPORT OpenCVToMitkImageFilter : public ImageSource { public: typedef itk::RGBPixel< unsigned char > UCRGBPixelType; typedef itk::RGBPixel< unsigned short > USRGBPixelType; typedef itk::RGBPixel< float > FloatRGBPixelType; typedef itk::RGBPixel< double > DoubleRGBPixelType; /// /// the static function for the conversion /// template static Image::Pointer ConvertCVMatToMitkImage(const cv::Mat input); mitkClassMacro(OpenCVToMitkImageFilter, ImageSource); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /// /// sets an iplimage as input /// void SetOpenCVImage(const IplImage* image); //itkGetMacro(OpenCVImage, const IplImage*); /// /// sets an opencv mat as input (will be used if OpenCVImage Ipl image is 0) /// void SetOpenCVMat(const cv::Mat& image); itkGetMacro(OpenCVMat, cv::Mat); OutputImageType* GetOutput(void); //##Documentation //## @brief Convenient method to insert an openCV image as a slice at a //## certain time step into a 3D or 4D mitk::Image. //## //## @param openCVImage - the image that is inserted into the mitk Image //## @param mitkImage - pointer to the mitkImage, which is changed by this method! //## @param timeStep - the time step, at which the openCVImage is inserted //## //## @attention The parameter mitkImage will be changed! void InsertOpenCVImageAsMitkTimeSlice(const cv::Mat openCVImage, Image::Pointer mitkImage, int timeStep); protected: OpenCVToMitkImageFilter(); // purposely hidden ~OpenCVToMitkImageFilter() override; void GenerateData() override; protected: Image::Pointer m_Image; cv::Mat m_OpenCVMat; - itk::FastMutexLock::Pointer m_ImageMutex; - itk::FastMutexLock::Pointer m_OpenCVMatMutex; + std::mutex m_ImageMutex; + std::mutex m_OpenCVMatMutex; }; } // namespace mitk #endif // mitkOpenCVToMitkImageFilter_h diff --git a/Modules/OpenIGTLink/Filters/mitkImageToIGTLMessageFilter.cpp b/Modules/OpenIGTLink/Filters/mitkImageToIGTLMessageFilter.cpp index 4ba7dcfabd..85ac128dc6 100644 --- a/Modules/OpenIGTLink/Filters/mitkImageToIGTLMessageFilter.cpp +++ b/Modules/OpenIGTLink/Filters/mitkImageToIGTLMessageFilter.cpp @@ -1,258 +1,262 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkImageToIGTLMessageFilter.h" #include "mitkImageReadAccessor.h" #include "itkByteSwapper.h" #include "igtlImageMessage.h" mitk::ImageToIGTLMessageFilter::ImageToIGTLMessageFilter() { mitk::IGTLMessage::Pointer output = mitk::IGTLMessage::New(); this->SetNumberOfRequiredOutputs(1); this->SetNthOutput(0, output.GetPointer()); this->SetNumberOfRequiredInputs(1); } void mitk::ImageToIGTLMessageFilter::GenerateData() { // MITK_INFO << "ImageToIGTLMessageFilter.GenerateData()"; for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs(); ++i) { mitk::IGTLMessage* output = this->GetOutput(i); assert(output); const mitk::Image* img = this->GetInput(i); int dims = img->GetDimension(); int chn = img->GetNumberOfChannels(); if (dims < 1) { MITK_ERROR << "Can not handle dimensionless images"; } if (dims > 3) { MITK_ERROR << "Can not handle more than three dimensions"; continue; } if (chn != 1) { MITK_ERROR << "Can not handle anything but one channel. Image contained " << chn; continue; } igtl::ImageMessage::Pointer imgMsg = igtl::ImageMessage::New(); // TODO: Which kind of coordinate system does MITK really use? imgMsg->SetCoordinateSystem(igtl::ImageMessage::COORDINATE_RAS); // We could do this based on the host endiannes, but that's weird. // We instead use little endian, as most modern systems are little endian, // so there will probably not be an endian swap involved. imgMsg->SetEndian(igtl::ImageMessage::ENDIAN_LITTLE); // Set number of components. mitk::PixelType type = img->GetPixelType(); imgMsg->SetNumComponents(type.GetNumberOfComponents()); // Set scalar type. switch (type.GetComponentType()) { - case itk::ImageIOBase::CHAR: + case itk::IOComponentEnum::CHAR: imgMsg->SetScalarTypeToInt8(); break; - case itk::ImageIOBase::UCHAR: + case itk::IOComponentEnum::UCHAR: imgMsg->SetScalarTypeToUint8(); break; - case itk::ImageIOBase::SHORT: + case itk::IOComponentEnum::SHORT: imgMsg->SetScalarTypeToInt16(); break; - case itk::ImageIOBase::USHORT: + case itk::IOComponentEnum::USHORT: imgMsg->SetScalarTypeToUint16(); break; - case itk::ImageIOBase::INT: + case itk::IOComponentEnum::INT: imgMsg->SetScalarTypeToInt32(); break; - case itk::ImageIOBase::UINT: + case itk::IOComponentEnum::UINT: imgMsg->SetScalarTypeToUint32(); break; - case itk::ImageIOBase::LONG: + case itk::IOComponentEnum::LONG: // OIGTL doesn't formally support 64bit int scalars, but if they are // ever added, // they will have the identifier 8 assigned. imgMsg->SetScalarType(8); break; - case itk::ImageIOBase::ULONG: + case itk::IOComponentEnum::ULONG: // OIGTL doesn't formally support 64bit uint scalars, but if they are // ever added, // they will have the identifier 9 assigned. imgMsg->SetScalarType(9); break; - case itk::ImageIOBase::FLOAT: + case itk::IOComponentEnum::FLOAT: // The igtl library has no method for this. Correct type is 10. imgMsg->SetScalarType(10); break; - case itk::ImageIOBase::DOUBLE: + case itk::IOComponentEnum::DOUBLE: // The igtl library has no method for this. Correct type is 11. imgMsg->SetScalarType(11); break; default: MITK_ERROR << "Can not handle pixel component type " << type.GetComponentType(); return; } // Set transformation matrix. vtkMatrix4x4* matrix = img->GetGeometry()->GetVtkMatrix(); float matF[4][4]; for (size_t i = 0; i < 4; ++i) { for (size_t j = 0; j < 4; ++j) { matF[i][j] = matrix->GetElement(i, j); } } imgMsg->SetMatrix(matF); float spacing[3]; auto spacingImg = img->GetGeometry()->GetSpacing(); for (int i = 0; i < 3; ++i) spacing[i] = spacingImg[i]; imgMsg->SetSpacing(spacing); // Set dimensions. int sizes[3]; for (size_t j = 0; j < 3; ++j) { sizes[j] = img->GetDimension(j); } imgMsg->SetDimensions(sizes); // Allocate and copy data. imgMsg->AllocatePack(); imgMsg->AllocateScalars(); size_t num_pixel = sizes[0] * sizes[1] * sizes[2]; void* out = imgMsg->GetScalarPointer(); { // Scoped, so that readAccess will be released ASAP. mitk::ImageReadAccessor readAccess(img, img->GetChannelData(0)); const void* in = readAccess.GetData(); memcpy(out, in, num_pixel * type.GetSize()); } // We want to byte swap to little endian. We would like to just // swap by number of bytes for each component, but itk::ByteSwapper // is templated over element type, not over element size. So we need to // switch on the size and use types of the same size. size_t num_scalars = num_pixel * type.GetNumberOfComponents(); switch (type.GetComponentType()) { - case itk::ImageIOBase::CHAR: - case itk::ImageIOBase::UCHAR: + case itk::IOComponentEnum::CHAR: + case itk::IOComponentEnum::UCHAR: // No endian conversion necessary, because a char is exactly one byte! break; - case itk::ImageIOBase::SHORT: - case itk::ImageIOBase::USHORT: + case itk::IOComponentEnum::SHORT: + case itk::IOComponentEnum::USHORT: itk::ByteSwapper::SwapRangeFromSystemToLittleEndian((short*)out, num_scalars); break; - case itk::ImageIOBase::INT: - case itk::ImageIOBase::UINT: + case itk::IOComponentEnum::INT: + case itk::IOComponentEnum::UINT: itk::ByteSwapper::SwapRangeFromSystemToLittleEndian((int*)out, num_scalars); break; - case itk::ImageIOBase::LONG: - case itk::ImageIOBase::ULONG: + case itk::IOComponentEnum::LONG: + case itk::IOComponentEnum::ULONG: itk::ByteSwapper::SwapRangeFromSystemToLittleEndian((long*)out, num_scalars); break; - case itk::ImageIOBase::FLOAT: + case itk::IOComponentEnum::FLOAT: itk::ByteSwapper::SwapRangeFromSystemToLittleEndian((float*)out, num_scalars); break; - case itk::ImageIOBase::DOUBLE: + case itk::IOComponentEnum::DOUBLE: itk::ByteSwapper::SwapRangeFromSystemToLittleEndian( (double*)out, num_scalars); break; + default: + MITK_ERROR << "Can not handle pixel component type " + << type.GetComponentType(); + return; } //copy timestamp of mitk image igtl::TimeStamp::Pointer timestamp = igtl::TimeStamp::New(); timestamp->SetTime(img->GetMTime() / 1000, (int)(img->GetMTime()) % 1000); imgMsg->SetTimeStamp(timestamp); imgMsg->Pack(); output->SetMessage(imgMsg.GetPointer()); } } void mitk::ImageToIGTLMessageFilter::SetInput(const mitk::Image* img) { this->ProcessObject::SetNthInput(0, const_cast(img)); this->CreateOutputsForAllInputs(); } void mitk::ImageToIGTLMessageFilter::SetInput(unsigned int idx, const Image* img) { this->ProcessObject::SetNthInput(idx, const_cast(img)); this->CreateOutputsForAllInputs(); } const mitk::Image* mitk::ImageToIGTLMessageFilter::GetInput(void) { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(0)); } const mitk::Image* mitk::ImageToIGTLMessageFilter::GetInput(unsigned int idx) { if (this->GetNumberOfInputs() < idx + 1) { return nullptr; } return static_cast(this->ProcessObject::GetInput(idx)); } void mitk::ImageToIGTLMessageFilter::ConnectTo(mitk::ImageSource* upstream) { MITK_INFO << "Image source for this (" << this << ") mitkImageToIGTLMessageFilter is " << upstream; for (DataObjectPointerArraySizeType i = 0; i < upstream->GetNumberOfOutputs(); i++) { this->SetInput(i, upstream->GetOutput(i)); } } void mitk::ImageToIGTLMessageFilter::CreateOutputsForAllInputs() { // create one message output for all image inputs this->SetNumberOfIndexedOutputs(this->GetNumberOfIndexedInputs()); for (size_t idx = 0; idx < this->GetNumberOfIndexedOutputs(); ++idx) { if (this->GetOutput(idx) == nullptr) { this->SetNthOutput(idx, this->MakeOutput(idx)); } this->Modified(); } } diff --git a/Modules/OpenIGTLink/mitkIGTLClient.cpp b/Modules/OpenIGTLink/mitkIGTLClient.cpp index 63bc2659fc..649c6a808d 100644 --- a/Modules/OpenIGTLink/mitkIGTLClient.cpp +++ b/Modules/OpenIGTLink/mitkIGTLClient.cpp @@ -1,120 +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 "mitkIGTLClient.h" //#include "mitkIGTTimeStamp.h" //#include "mitkIGTHardwareException.h" #include "igtlTrackingDataMessage.h" #include #include -#include #include #include -typedef itk::MutexLockHolder MutexLockHolder; - mitk::IGTLClient::IGTLClient(bool ReadFully) : IGTLDevice(ReadFully) { } mitk::IGTLClient::~IGTLClient() { } bool mitk::IGTLClient::OpenConnection() { if (this->GetState() != Setup) { mitkThrowException(mitk::Exception) << "Can only try to open the connection if in setup mode. State was " << this->GetState(); return false; } std::string hostname = this->GetHostname(); int portNumber = this->GetPortNumber(); if (portNumber == -1 || hostname.size() <= 0) { //port number or hostname was not correct MITK_WARN << "Port number or hostname was not correct"; return false; } //create a new client socket m_Socket = igtl::ClientSocket::New(); //try to connect to the igtl server int response = dynamic_cast(m_Socket.GetPointer())-> ConnectToServer(hostname.c_str(), portNumber); //check the response if (response != 0) { MITK_ERROR << "The client could not connect to " << hostname << " port: " << portNumber; return false; } // everything is initialized and connected so the communication can be started this->SetState(Ready); //inform observers about this new client this->InvokeEvent(NewClientConnectionEvent()); return true; } void mitk::IGTLClient::Receive() { //MITK_INFO << "Trying to receive message"; //try to receive a message, if the socket is not present anymore stop the //communication unsigned int status = this->ReceivePrivate(this->m_Socket); if (status == IGTL_STATUS_NOT_PRESENT) { this->StopCommunicationWithSocket(this->m_Socket); //inform observers about loosing the connection to this socket this->InvokeEvent(LostConnectionEvent()); MITK_WARN("IGTLClient") << "Lost connection to server socket."; } } void mitk::IGTLClient::Send() { mitk::IGTLMessage::Pointer mitkMessage; //get the latest message from the queue mitkMessage = this->m_MessageQueue->PullSendMessage(); // there is no message => return if (mitkMessage.IsNull()) return; if (!this->SendMessagePrivate(mitkMessage, this->m_Socket)) { MITK_WARN("IGTLDevice") << "Could not send the message."; } } void mitk::IGTLClient::StopCommunicationWithSocket(igtl::Socket* /*socket*/) { - m_StopCommunicationMutex->Lock(); + m_StopCommunicationMutex.lock(); m_StopCommunication = true; - m_StopCommunicationMutex->Unlock(); + m_StopCommunicationMutex.unlock(); } unsigned int mitk::IGTLClient::GetNumberOfConnections() { return this->m_Socket->GetConnected(); } diff --git a/Modules/OpenIGTLink/mitkIGTLDevice.cpp b/Modules/OpenIGTLink/mitkIGTLDevice.cpp index 5cb079b7cd..8d531d085d 100644 --- a/Modules/OpenIGTLink/mitkIGTLDevice.cpp +++ b/Modules/OpenIGTLink/mitkIGTLDevice.cpp @@ -1,559 +1,496 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkIGTLDevice.h" //#include "mitkIGTException.h" //#include "mitkIGTTimeStamp.h" -#include +#include #include #include +#include #include #include #include //remove later #include +namespace mitk +{ + itkEventMacroDefinition(MessageSentEvent, itk::AnyEvent); + itkEventMacroDefinition(MessageReceivedEvent, itk::AnyEvent); + itkEventMacroDefinition(CommandReceivedEvent, itk::AnyEvent); + itkEventMacroDefinition(NewClientConnectionEvent, itk::AnyEvent); + itkEventMacroDefinition(LostConnectionEvent, itk::AnyEvent); +} + //TODO: Which timeout is acceptable and also needed to transmit image data? Is there a maximum data limit? static const int SOCKET_SEND_RECEIVE_TIMEOUT_MSEC = 100; -typedef itk::MutexLockHolder MutexLockHolder; mitk::IGTLDevice::IGTLDevice(bool ReadFully) : // m_Data(mitk::DeviceDataUnspecified), m_State(mitk::IGTLDevice::Setup), m_Name("Unspecified Device"), m_StopCommunication(false), m_Hostname("127.0.0.1"), m_PortNumber(-1), -m_LogMessages(false), -m_MultiThreader(nullptr), m_SendThreadID(0), m_ReceiveThreadID(0), m_ConnectThreadID(0) +m_LogMessages(false) { m_ReadFully = ReadFully; - m_StopCommunicationMutex = itk::FastMutexLock::New(); - m_StateMutex = itk::FastMutexLock::New(); - // m_LatestMessageMutex = itk::FastMutexLock::New(); - m_SendingFinishedMutex = itk::FastMutexLock::New(); - m_ReceivingFinishedMutex = itk::FastMutexLock::New(); - m_ConnectingFinishedMutex = itk::FastMutexLock::New(); // execution rights are owned by the application thread at the beginning - m_SendingFinishedMutex->Lock(); - m_ReceivingFinishedMutex->Lock(); - m_ConnectingFinishedMutex->Lock(); - m_MultiThreader = itk::MultiThreader::New(); + m_SendingFinishedMutex.lock(); + m_ReceivingFinishedMutex.lock(); + m_ConnectingFinishedMutex.lock(); // m_Data = mitk::DeviceDataUnspecified; // m_LatestMessage = igtl::MessageBase::New(); m_MessageFactory = mitk::IGTLMessageFactory::New(); m_MessageQueue = mitk::IGTLMessageQueue::New(); } mitk::IGTLDevice::~IGTLDevice() { /* stop communication and disconnect from igtl device */ if (GetState() == Running) { this->StopCommunication(); this->CloseConnection(); } else if (GetState() == Ready) { this->CloseConnection(); } /* cleanup tracking thread */ - if (m_MultiThreader.IsNotNull()) - { - if ((m_SendThreadID != 0)) - { - m_MultiThreader->TerminateThread(m_SendThreadID); - } - if ((m_ReceiveThreadID != 0)) - { - m_MultiThreader->TerminateThread(m_ReceiveThreadID); - } - if ((m_ConnectThreadID != 0)) - { - m_MultiThreader->TerminateThread(m_ConnectThreadID); - } - } - m_MultiThreader = nullptr; + if (m_SendThread.joinable()) + m_SendThread.detach(); + + if (m_ReceiveThread.joinable()) + m_ReceiveThread.detach(); + + if (m_ConnectThread.joinable()) + m_ConnectThread.detach(); } mitk::IGTLDevice::IGTLDeviceState mitk::IGTLDevice::GetState() const { - MutexLockHolder lock(*m_StateMutex); + std::lock_guard lock(m_StateMutex); return m_State; } void mitk::IGTLDevice::SetState(IGTLDeviceState state) { itkDebugMacro("setting m_State to " << state); - m_StateMutex->Lock(); + m_StateMutex.lock(); // MutexLockHolder lock(*m_StateMutex); // lock and unlock the mutex if (m_State == state) { - m_StateMutex->Unlock(); + m_StateMutex.unlock(); return; } m_State = state; - m_StateMutex->Unlock(); + m_StateMutex.unlock(); this->Modified(); } bool mitk::IGTLDevice::TestConnection() { return true; } unsigned int mitk::IGTLDevice::ReceivePrivate(igtl::Socket* socket) { // Create a message buffer to receive header igtl::MessageHeader::Pointer headerMsg; headerMsg = igtl::MessageHeader::New(); // Initialize receive buffer headerMsg->InitPack(); // Receive generic header from the socket int r = socket->Receive(headerMsg->GetPackPointer(), headerMsg->GetPackSize(), 0); //MITK_INFO << "Server received r = " << r; //MITK_INFO << "Received r = " << r; if (r == 0) //connection error { // an error was received, therefore the communication with this socket // must be stoppedy return IGTL_STATUS_NOT_PRESENT; } else if (r == -1) //timeout { // a timeout was received, this is no error state, thus, do nothing return IGTL_STATUS_TIME_OUT; } else if (r == headerMsg->GetPackSize()) { // Deserialize the header and check the CRC // ERROR HERE: This probably means the header data is corrupted... int crcCheck = headerMsg->Unpack(1); if (crcCheck & igtl::MessageHeader::UNPACK_HEADER) { // Allocate a time stamp igtl::TimeStamp::Pointer ts; ts = igtl::TimeStamp::New(); // Get time stamp igtlUint32 sec; igtlUint32 nanosec; headerMsg->GetTimeStamp(ts); ts->GetTimeStamp(&sec, &nanosec); // std::cerr << "Time stamp: " // << sec << "." // << nanosec << std::endl; // std::cerr << "Dev type and name: " << headerMsg->GetDeviceType() << " " // << headerMsg->GetDeviceName() << std::endl; // headerMsg->Print(std::cout); //check the type of the received message //if it is a GET_, STP_ or RTS_ command push it into the command queue //otherwise continue reading the whole message from the socket const char* curDevType = headerMsg->GetDeviceType(); if (std::strstr(curDevType, "GET_") != nullptr || std::strstr(curDevType, "STP_") != nullptr || std::strstr(curDevType, "RTS_") != nullptr) { this->m_MessageQueue->PushCommandMessage(headerMsg); this->InvokeEvent(CommandReceivedEvent()); return IGTL_STATUS_OK; } //Create a message according to the header message igtl::MessageBase::Pointer curMessage; curMessage = m_MessageFactory->CreateInstance(headerMsg); //check if the curMessage is created properly, if not the message type is //not supported and the message has to be skipped if (curMessage.IsNull()) { socket->Skip(headerMsg->GetBodySizeToRead(), 0); // MITK_ERROR("IGTLDevice") << "The received type is not supported. Please " // "add it to the message factory."; return IGTL_STATUS_NOT_FOUND; } //insert the header to the message and allocate the pack curMessage->SetMessageHeader(headerMsg); curMessage->AllocatePack(); // Receive transform data from the socket int receiveCheck = 0; receiveCheck = socket->Receive(curMessage->GetPackBodyPointer(), curMessage->GetPackBodySize(), m_ReadFully); if (receiveCheck > 0) { int c = curMessage->Unpack(1); if (!(c & igtl::MessageHeader::UNPACK_BODY)) { return IGTL_STATUS_CHECKSUM_ERROR; } //check the type of the received message //if it is a command push it into the command queue //otherwise into the normal receive queue //STP_ commands are handled here because they implemented additional //member variables that are not stored in the header message if (std::strstr(curDevType, "STT_") != nullptr) { this->m_MessageQueue->PushCommandMessage(curMessage); this->InvokeEvent(CommandReceivedEvent()); } else { if(m_LogMessages) MITK_INFO << "Received Message: " << mitk::IGTLMessage::New(curMessage)->ToString(); this->m_MessageQueue->PushMessage(curMessage); this->InvokeEvent(MessageReceivedEvent()); } return IGTL_STATUS_OK; } else { MITK_WARN("IGTLDevice") << "Received a valid header but could not " << "read the whole message."; return IGTL_STATUS_UNKNOWN_ERROR; } } else { //CRC check failed MITK_WARN << "CRC Check failed"; return IGTL_STATUS_CHECKSUM_ERROR; } } else { //Message size information and actual data size don't match. //this state is not suppossed to be reached, return unknown error MITK_WARN << "IGTL status unknown"; return IGTL_STATUS_UNKNOWN_ERROR; } } void mitk::IGTLDevice::SendMessage(mitk::IGTLMessage::Pointer msg) { m_MessageQueue->PushSendMessage(msg); } unsigned int mitk::IGTLDevice::SendMessagePrivate(mitk::IGTLMessage::Pointer msg, igtl::Socket::Pointer socket) { //check the input message if (msg.IsNull()) { MITK_ERROR("IGTLDevice") << "Could not send message because message is not " "valid. Please check."; return false; } igtl::MessageBase* sendMessage = msg->GetMessage(); // Pack (serialize) and send sendMessage->Pack(); int sendSuccess = socket->Send(sendMessage->GetPackPointer(), sendMessage->GetPackSize()); if (sendSuccess) { if (m_LogMessages) { MITK_INFO << "Send IGTL message: " << msg->ToString(); } this->InvokeEvent(MessageSentEvent()); return IGTL_STATUS_OK; } else { return IGTL_STATUS_UNKNOWN_ERROR; } } -void mitk::IGTLDevice::RunCommunication(void (IGTLDevice::*ComFunction)(void), itk::FastMutexLock* mutex) +void mitk::IGTLDevice::RunCommunication(void (IGTLDevice::*ComFunction)(void), std::mutex& mutex) { if (this->GetState() != Running) return; try { // keep lock until end of scope - MutexLockHolder communicationFinishedLockHolder(*mutex); + std::lock_guard communicationFinishedLockHolder(mutex); // Because m_StopCommunication is used by two threads, access has to be guarded // by a mutex. To minimize thread locking, a local copy is used here bool localStopCommunication; // update the local copy of m_StopCommunication - this->m_StopCommunicationMutex->Lock(); + this->m_StopCommunicationMutex.lock(); localStopCommunication = this->m_StopCommunication; - this->m_StopCommunicationMutex->Unlock(); + this->m_StopCommunicationMutex.unlock(); while ((this->GetState() == Running) && (localStopCommunication == false)) { (this->*ComFunction)(); /* Update the local copy of m_StopCommunication */ - this->m_StopCommunicationMutex->Lock(); + this->m_StopCommunicationMutex.lock(); localStopCommunication = m_StopCommunication; - this->m_StopCommunicationMutex->Unlock(); + this->m_StopCommunicationMutex.unlock(); // time to relax, this sets the maximum ever possible framerate to 1000 Hz itksys::SystemTools::Delay(1); } } catch (...) { - mutex->Unlock(); + mutex.unlock(); this->StopCommunication(); MITK_ERROR("IGTLDevice::RunCommunication") << "Error while communicating. Thread stopped."; //mitkThrowException(mitk::IGTException) << "Error while communicating. Thread stopped."; } // StopCommunication was called, thus the mode should be changed back to Ready now // that the tracking loop has ended. //this->SetState(Ready); //this is done elsewhere MITK_DEBUG("IGTLDevice::RunCommunication") << "Reached end of communication."; // returning from this function (and ThreadStartCommunication()) // this will end the thread return; } bool mitk::IGTLDevice::StartCommunication() { if (this->GetState() != Ready) return false; // go to mode Running this->SetState(Running); // set a timeout for the sending and receiving this->m_Socket->SetTimeout(SOCKET_SEND_RECEIVE_TIMEOUT_MSEC); // update the local copy of m_StopCommunication - this->m_StopCommunicationMutex->Lock(); + this->m_StopCommunicationMutex.lock(); this->m_StopCommunication = false; - this->m_StopCommunicationMutex->Unlock(); + this->m_StopCommunicationMutex.unlock(); // transfer the execution rights to tracking thread - m_SendingFinishedMutex->Unlock(); - m_ReceivingFinishedMutex->Unlock(); - m_ConnectingFinishedMutex->Unlock(); + m_SendingFinishedMutex.unlock(); + m_ReceivingFinishedMutex.unlock(); + m_ConnectingFinishedMutex.unlock(); // start new threads that execute the communication - m_SendThreadID = - m_MultiThreader->SpawnThread(this->ThreadStartSending, this); - m_ReceiveThreadID = - m_MultiThreader->SpawnThread(this->ThreadStartReceiving, this); - m_ConnectThreadID = - m_MultiThreader->SpawnThread(this->ThreadStartConnecting, this); + m_SendThread = std::thread(&IGTLDevice::ThreadStartSending, this); + m_ReceiveThread = std::thread(&IGTLDevice::ThreadStartReceiving, this); + m_ConnectThread = std::thread(&IGTLDevice::ThreadStartConnecting, this); // mitk::IGTTimeStamp::GetInstance()->Start(this); return true; } bool mitk::IGTLDevice::StopCommunication() { if (this->GetState() == Running) // Only if the object is in the correct state { // m_StopCommunication is used by two threads, so we have to ensure correct // thread handling - m_StopCommunicationMutex->Lock(); + m_StopCommunicationMutex.lock(); m_StopCommunication = true; - m_StopCommunicationMutex->Unlock(); + m_StopCommunicationMutex.unlock(); // we have to wait here that the other thread recognizes the STOP-command // and executes it - m_SendingFinishedMutex->Lock(); - m_ReceivingFinishedMutex->Lock(); - m_ConnectingFinishedMutex->Lock(); + m_SendingFinishedMutex.lock(); + m_ReceivingFinishedMutex.lock(); + m_ConnectingFinishedMutex.lock(); // mitk::IGTTimeStamp::GetInstance()->Stop(this); // notify realtime clock // StopCommunication was called, thus the mode should be changed back // to Ready now that the tracking loop has ended. this->SetState(Ready); } return true; } bool mitk::IGTLDevice::CloseConnection() { if (this->GetState() == Setup) { return true; } else if (this->GetState() == Running) { this->StopCommunication(); } m_Socket->CloseSocket(); /* return to setup mode */ this->SetState(Setup); // this->InvokeEvent(mitk::LostConnectionEvent()); return true; } bool mitk::IGTLDevice::SendRTSMessage(const char* type) { //construct the device type for the return message, it starts with RTS_ and //continues with the requested type std::string returnType("RTS_"); returnType.append(type); //create a return message igtl::MessageBase::Pointer rtsMsg = this->m_MessageFactory->CreateInstance(returnType); //if retMsg is nullptr there is no return message defined and thus it is not //necessary to send one back if (rtsMsg.IsNotNull()) { this->SendMessage(mitk::IGTLMessage::New(rtsMsg)); return true; } else { return false; } } void mitk::IGTLDevice::Connect() { MITK_DEBUG << "mitk::IGTLDevice::Connect();"; } igtl::ImageMessage::Pointer mitk::IGTLDevice::GetNextImage2dMessage() { return this->m_MessageQueue->PullImage2dMessage(); } igtl::ImageMessage::Pointer mitk::IGTLDevice::GetNextImage3dMessage() { return this->m_MessageQueue->PullImage3dMessage(); } igtl::TransformMessage::Pointer mitk::IGTLDevice::GetNextTransformMessage() { return this->m_MessageQueue->PullTransformMessage(); } igtl::TrackingDataMessage::Pointer mitk::IGTLDevice::GetNextTrackingDataMessage() { igtl::TrackingDataMessage::Pointer msg = this->m_MessageQueue->PullTrackingMessage(); return msg; } igtl::StringMessage::Pointer mitk::IGTLDevice::GetNextStringMessage() { return this->m_MessageQueue->PullStringMessage(); } igtl::MessageBase::Pointer mitk::IGTLDevice::GetNextMiscMessage() { return this->m_MessageQueue->PullMiscMessage(); } igtl::MessageBase::Pointer mitk::IGTLDevice::GetNextCommand() { return m_MessageQueue->PullCommandMessage(); } void mitk::IGTLDevice::EnableNoBufferingMode(bool enable) { m_MessageQueue->EnableNoBufferingMode(enable); } void mitk::IGTLDevice::EnableNoBufferingMode( mitk::IGTLMessageQueue::Pointer queue, bool enable) { queue->EnableNoBufferingMode(enable); } -ITK_THREAD_RETURN_TYPE mitk::IGTLDevice::ThreadStartSending(void* pInfoStruct) +void mitk::IGTLDevice::ThreadStartSending() { - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct * pInfo = - (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - if (pInfo == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - IGTLDevice *igtlDevice = (IGTLDevice*)pInfo->UserData; - if (igtlDevice != nullptr) - { - igtlDevice->RunCommunication(&mitk::IGTLDevice::Send, igtlDevice->m_SendingFinishedMutex); - } - igtlDevice->m_SendThreadID = 0; // erase thread id because thread will end. - return ITK_THREAD_RETURN_VALUE; + this->RunCommunication(&IGTLDevice::Send, m_SendingFinishedMutex); } -ITK_THREAD_RETURN_TYPE mitk::IGTLDevice::ThreadStartReceiving(void* pInfoStruct) +void mitk::IGTLDevice::ThreadStartReceiving() { - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct * pInfo = - (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - if (pInfo == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - IGTLDevice *igtlDevice = (IGTLDevice*)pInfo->UserData; - if (igtlDevice != nullptr) - { - igtlDevice->RunCommunication(&mitk::IGTLDevice::Receive, - igtlDevice->m_ReceivingFinishedMutex); - } - igtlDevice->m_ReceiveThreadID = 0; // erase thread id because thread will end. - return ITK_THREAD_RETURN_VALUE; + this->RunCommunication(&IGTLDevice::Receive, m_ReceivingFinishedMutex); } -ITK_THREAD_RETURN_TYPE mitk::IGTLDevice::ThreadStartConnecting(void* pInfoStruct) +void mitk::IGTLDevice::ThreadStartConnecting() { - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct * pInfo = - (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - if (pInfo == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - IGTLDevice *igtlDevice = (IGTLDevice*)pInfo->UserData; - if (igtlDevice != nullptr) - { - igtlDevice->RunCommunication(&mitk::IGTLDevice::Connect, - igtlDevice->m_ConnectingFinishedMutex); - } - igtlDevice->m_ConnectThreadID = 0; // erase thread id because thread will end. - return ITK_THREAD_RETURN_VALUE; + this->RunCommunication(&IGTLDevice::Connect, m_ConnectingFinishedMutex); } diff --git a/Modules/OpenIGTLink/mitkIGTLDevice.h b/Modules/OpenIGTLink/mitkIGTLDevice.h index 57695c0f64..684d5953f2 100644 --- a/Modules/OpenIGTLink/mitkIGTLDevice.h +++ b/Modules/OpenIGTLink/mitkIGTLDevice.h @@ -1,413 +1,408 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKIGTLDEVICE_H #define MITKIGTLDEVICE_H +#include +#include + #include "mitkCommon.h" //itk #include "itkObject.h" -#include "itkFastMutexLock.h" -#include "itkMultiThreader.h" //igtl #include "igtlSocket.h" #include "igtlMessageBase.h" #include "igtlTransformMessage.h" //mitkIGTL #include "MitkOpenIGTLinkExports.h" #include "mitkIGTLMessageFactory.h" #include "mitkIGTLMessageQueue.h" #include "mitkIGTLMessage.h" namespace mitk { /** * \brief Interface for all OpenIGTLink Devices * * Defines the methods that are common for all devices using OpenIGTLink. It * can open/close a connection, start/stop a communication and send/receive * messages. * * It uses message queues to store the incoming and outgoing mails. They are * configurable, you can set buffering on and off. * * The device is in one of three different states: Setup, Ready or Running. * Setup is the initial state. From this state on you can call * OpenConnection() and arrive in the Ready state. From the Ready state you * call StartCommunication() to arrive in the Running state. Now the device * is continuosly checking for new connections, receiving messages and * sending messages. This runs in a seperate thread. To stop the communication * call StopCommunication() (to arrive in Ready state) or CloseConnection() * (to arrive in the Setup state). * * \ingroup OpenIGTLink * */ class MITKOPENIGTLINK_EXPORT IGTLDevice : public itk::Object { public: mitkClassMacroItkParent(IGTLDevice, itk::Object); IGTLDevice(bool ReadFully); /** * \brief Type for state variable. * The IGTLDevice is always in one of these states. * */ enum IGTLDeviceState { Setup, Ready, Running }; /** * \brief Opens a connection to the device * * This may only be called if there is currently no connection to the * device. If OpenConnection() is successful, the object will change from * Setup state to Ready state. */ virtual bool OpenConnection() = 0; /** * \brief Closes the connection to the device * * This may only be called if there is currently a connection to the * device, but device is not running (e.g. object is in Ready state) */ virtual bool CloseConnection(); /** * \brief Stops the communication between the two devices * * This may only be called if the device is in Running state. */ virtual bool StopCommunication(); /** * \brief Starts the communication between the two devices * * This may only be called if the device is in Ready state. */ bool StartCommunication(); /** * \brief Continuously calls the given function * * This may only be called if the device is in Running state and only from * a seperate thread. * * \param ComFunction function pointer that specifies the method to be executed * \param mutex the mutex that corresponds to the function pointer */ - void RunCommunication(void (IGTLDevice::*ComFunction)(void), itk::FastMutexLock* mutex); + void RunCommunication(void (IGTLDevice::*ComFunction)(void), std::mutex& mutex); /** * \brief Adds the given message to the sending queue * * This may only be called after the connection to the device has been * established with a call to OpenConnection(). Note that the message * is not send directly. This method just adds it to the send queue. * \param msg The message to be added to the sending queue */ void SendMessage(mitk::IGTLMessage::Pointer msg); /** * \brief Returns current object state (Setup, Ready or Running) */ IGTLDeviceState GetState() const; /** * \brief Returns the oldest message in the command queue * \return The oldest message from the command queue. */ igtl::MessageBase::Pointer GetNextCommand(); /** * \brief Returns the oldest message in the receive queue * \return The oldest message from the receive queue */ igtl::ImageMessage::Pointer GetNextImage2dMessage(); igtl::ImageMessage::Pointer GetNextImage3dMessage(); igtl::TransformMessage::Pointer GetNextTransformMessage(); igtl::TrackingDataMessage::Pointer GetNextTrackingDataMessage(); igtl::StringMessage::Pointer GetNextStringMessage(); igtl::MessageBase::Pointer GetNextMiscMessage(); /** * \brief Sets the port number of the device */ itkSetMacro(PortNumber, int); /** * \brief Returns the port number of the device */ itkGetMacro(PortNumber, int); /** * \brief Sets the ip/hostname of the device */ itkSetMacro(Hostname, std::string); /** * \brief Returns the ip/hostname of the device */ itkGetMacro(Hostname, std::string); /** * \brief Returns the name of this device */ itkGetConstMacro(Name, std::string); /** * \brief Sets the name of this device */ itkSetMacro(Name, std::string); /** * \brief Advises this IGTL Device to always block until the whole message is read. */ itkSetMacro(ReadFully, bool); /** * \brief Returns a const reference to the receive queue */ itkGetConstMacro(MessageQueue, mitk::IGTLMessageQueue::Pointer); /** * \brief Returns the message factory */ itkGetMacro(MessageFactory, mitk::IGTLMessageFactory::Pointer); /** - * \brief static start method for the sending thread. - * \param data a void pointer to the IGTLDevice object. + * \brief start method for the sending thread. */ - static ITK_THREAD_RETURN_TYPE ThreadStartSending(void* data); + void ThreadStartSending(); /** - * \brief static start method for the receiving thread. - * \param data a void pointer to the IGTLDevice object. + * \brief start method for the receiving thread. */ - static ITK_THREAD_RETURN_TYPE ThreadStartReceiving(void* data); + void ThreadStartReceiving(); /** - * \brief static start method for the connection thread. - * \param data a void pointer to the IGTLDevice object. + * \brief start method for the connection thread. */ - static ITK_THREAD_RETURN_TYPE ThreadStartConnecting(void* data); + void ThreadStartConnecting(); /** * \brief TestConnection() tries to connect to a IGTL device on the current * ip and port * * \todo Implement this method. Send a status message and check the answer. * * TestConnection() tries to connect to a IGTL server on the current * ip and port and returns which device it has found. * \return It returns the type of the device that answers. Throws an * exception * if no device is available on that ip/port. * @throw mitk::Exception Throws an exception if there are errors * while connecting to the device. */ virtual bool TestConnection(); /** * \brief Send RTS message of given type */ bool SendRTSMessage(const char* type); /** * \brief Sets the buffering mode of the given queue */ void EnableNoBufferingMode(mitk::IGTLMessageQueue::Pointer queue, bool enable = true); void EnableNoBufferingMode(bool enable = true); /** * \brief Returns the number of connections of this device */ virtual unsigned int GetNumberOfConnections() = 0; itkGetMacro(LogMessages, bool); itkSetMacro(LogMessages, bool); protected: /** * \brief Sends a message. * * This may only be called after the connection to the device has been * established with a call to OpenConnection(). This method uses the given * socket to send the given MessageReceivedEvent * * \param msg the message to be sent * \param socket the socket used to communicate with the other device * * \retval IGTL_STATUS_OK the message was sent * \retval IGTL_STATUS_UNKONWN_ERROR the message was not sent because an * unknown error occurred */ unsigned int SendMessagePrivate(mitk::IGTLMessage::Pointer msg, igtl::Socket::Pointer socket); /** * \brief Call this method to receive a message. * * The message will be saved in the receive queue. */ virtual void Receive() = 0; /** * \brief Call this method to receive a message from the given device. * * The message will be saved in the receive queue. * * \param device the socket that connects this device with the other one. * * \retval IGTL_STATUS_OK a message or a command was received * \retval IGTL_STATUS_NOT_PRESENT the socket is not connected anymore * \retval IGTL_STATUS_TIME_OUT the socket timed out * \retval IGTL_STATUS_CHECKSUM_ERROR the checksum of the received msg was * incorrect * \retval IGTL_STATUS_UNKNOWN_ERROR an unknown error occurred */ unsigned int ReceivePrivate(igtl::Socket* device); /** * \brief Call this method to send a message. The message will be read from * the queue. */ virtual void Send() = 0; /** * \brief Call this method to check for other devices that want to connect * to this one. * * In case of a client this method is doing nothing. In case of a server it * is checking for other devices and if there is one it establishes a * connection. */ virtual void Connect(); /** * \brief Stops the communication with the given socket * */ virtual void StopCommunicationWithSocket(igtl::Socket* socket) = 0; /** * \brief change object state */ void SetState(IGTLDeviceState state); IGTLDevice(); ~IGTLDevice() override; /** current object state (Setup, Ready or Running) */ IGTLDeviceState m_State; /** the name of this device */ std::string m_Name; /** signal used to stop the thread*/ bool m_StopCommunication; /** mutex to control access to m_StopCommunication */ - itk::FastMutexLock::Pointer m_StopCommunicationMutex; + std::mutex m_StopCommunicationMutex; /** mutex used to make sure that the send thread is just started once */ - itk::FastMutexLock::Pointer m_SendingFinishedMutex; + std::mutex m_SendingFinishedMutex; /** mutex used to make sure that the receive thread is just started once */ - itk::FastMutexLock::Pointer m_ReceivingFinishedMutex; + std::mutex m_ReceivingFinishedMutex; /** mutex used to make sure that the connect thread is just started once */ - itk::FastMutexLock::Pointer m_ConnectingFinishedMutex; + std::mutex m_ConnectingFinishedMutex; /** mutex to control access to m_State */ - itk::FastMutexLock::Pointer m_StateMutex; + mutable std::mutex m_StateMutex; /** the hostname or ip of the device */ std::string m_Hostname; /** the port number of the device */ int m_PortNumber; /** the socket used to communicate with other IGTL devices */ igtl::Socket::Pointer m_Socket; /** The message receive queue */ mitk::IGTLMessageQueue::Pointer m_MessageQueue; /** A message factory that provides the New() method for all msg types */ mitk::IGTLMessageFactory::Pointer m_MessageFactory; bool m_LogMessages; private: - /** creates worker thread that continuously polls interface for new - messages */ - itk::MultiThreader::Pointer m_MultiThreader; - /** ID of sending thread */ - int m_SendThreadID; - /** ID of receiving thread */ - int m_ReceiveThreadID; - /** ID of connecting thread */ - int m_ConnectThreadID; + /** Sending thread */ + std::thread m_SendThread; + /** Receiving thread */ + std::thread m_ReceiveThread; + /** Connecting thread */ + std::thread m_ConnectThread; /** Always try to read the full message. */ bool m_ReadFully; }; /** * \brief connect to this Event to get notified when a message was successfully sent * * \note This event is invoked in the communication thread, therefore do not use it to make * changes in the GUI!!! Use the QT signal slot system to decouple this call from the com thread * */ - itkEventMacro(MessageSentEvent, itk::AnyEvent); + itkEventMacroDeclaration(MessageSentEvent, itk::AnyEvent); /** * \brief connect to this Event to get notified when a message was received * * \note Check if you can invoke this events like this or if you have to make * it thread-safe. They are not invoked in the main thread!!! * */ - itkEventMacro(MessageReceivedEvent, itk::AnyEvent); + itkEventMacroDeclaration(MessageReceivedEvent, itk::AnyEvent); /** * \brief connect to this Event to get notified when a command was received * * \note Check if you can invoke this events like this or if you have to make * it thread-safe. They are not invoked in the main thread!!! * */ - itkEventMacro(CommandReceivedEvent, itk::AnyEvent); + itkEventMacroDeclaration(CommandReceivedEvent, itk::AnyEvent); /** * \brief connect to this Event to get notified when another igtl device * connects with this device. * * \note Check if you can invoke this events like this or if you have to make * it thread-safe. They are not invoked in the main thread!!! * */ - itkEventMacro(NewClientConnectionEvent, itk::AnyEvent); + itkEventMacroDeclaration(NewClientConnectionEvent, itk::AnyEvent); /** * \brief connect to this Event to get notified when this device looses the * connection to a socket. * * \note Check if you can invoke this events like this or if you have to make * it thread-safe. They are not invoked in the main thread!!! * */ - itkEventMacro(LostConnectionEvent, itk::AnyEvent); + itkEventMacroDeclaration(LostConnectionEvent, itk::AnyEvent); } // namespace mitk #endif /* MITKIGTLDEVICE_H */ diff --git a/Modules/OpenIGTLink/mitkIGTLMessageProvider.cpp b/Modules/OpenIGTLink/mitkIGTLMessageProvider.cpp index 5ebfdad595..5217d8b932 100644 --- a/Modules/OpenIGTLink/mitkIGTLMessageProvider.cpp +++ b/Modules/OpenIGTLink/mitkIGTLMessageProvider.cpp @@ -1,444 +1,383 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkIGTLMessageProvider.h" #include "mitkIGTLDevice.h" #include "mitkIGTLMessage.h" #include "mitkIGTLMessageFactory.h" #include "mitkCallbackFromGUIThread.h" //Microservices #include "usServiceReference.h" #include "usModuleContext.h" #include "usServiceEvent.h" #include "mitkServiceInterface.h" #include "usGetModuleContext.h" //igt (remove this later) #include "igtlBindMessage.h" #include "igtlQuaternionTrackingDataMessage.h" #include "igtlTrackingDataMessage.h" #ifndef WIN32 #include #endif +namespace mitk +{ + itkEventMacroDefinition(StreamingStartRequiredEvent, itk::AnyEvent); + itkEventMacroDefinition(StreamingStopRequiredEvent, itk::AnyEvent); +} + mitk::IGTLMessageProvider::IGTLMessageProvider() : mitk::IGTLDeviceSource() { this->SetName("IGTLMessageProvider"); - //m_MultiThreader = itk::MultiThreader::New(); - m_StreamingTimeMutex = itk::FastMutexLock::New(); - //m_StopStreamingThreadMutex = itk::FastMutexLock::New(); - //m_ThreadId = 0; m_IsStreaming = false; // Create a command object. The function will be called later from the main thread this->m_StopStreamingCommand = ProviderCommand::New(); m_StopStreamingCommand->SetCallbackFunction(this, &mitk::IGTLMessageProvider::InvokeStopStreamingEvent); this->m_StreamingCommand = ProviderCommand::New(); m_StreamingCommand->SetCallbackFunction(this, &mitk::IGTLMessageProvider::InvokeStartStreamingEvent); } mitk::IGTLMessageProvider::~IGTLMessageProvider() { - //// terminate worker thread on destruction - //this->m_StopStreamingThreadMutex->Lock(); - //this->m_StopStreamingThread = true; - //this->m_StopStreamingThreadMutex->Unlock(); - //if ( m_ThreadId >= 0) - //{ - // this->m_MultiThreader->TerminateThread(m_ThreadId); - //} - this->InvokeEvent(StreamingStartRequiredEvent()); + this->InvokeEvent(StreamingStartRequiredEvent()); } void mitk::IGTLMessageProvider::Update() { Superclass::Update(); if (this->GetInput() != nullptr) { igtl::MessageBase::Pointer curMessage = this->GetInput()->GetMessage(); if (dynamic_cast(curMessage.GetPointer()) != nullptr) { igtl::TrackingDataMessage* tdMsg = (igtl::TrackingDataMessage*)(curMessage.GetPointer()); igtl::TrackingDataElement::Pointer trackingData = igtl::TrackingDataElement::New(); tdMsg->GetTrackingDataElement(0, trackingData); float x_pos, y_pos, z_pos; trackingData->GetPosition(&x_pos, &y_pos, &z_pos); } } } void mitk::IGTLMessageProvider::GenerateData() { if (this->m_IGTLDevice.IsNull()) return; for (unsigned int index = 0; index < this->GetNumberOfIndexedInputs(); index++) { mitk::IGTLMessage::Pointer msg = const_cast(this->GetInput(index)); if (msg == nullptr) { continue; } if ( !msg->IsDataValid() ) { continue; } this->m_IGTLDevice->SendMessage(msg); } } void mitk::IGTLMessageProvider::CreateOutputs() { //if outputs are set then delete them if (this->GetNumberOfOutputs() > 0) { for (int numOP = this->GetNumberOfOutputs() - 1; numOP >= 0; numOP--) this->RemoveOutput(numOP); this->Modified(); } //fill the outputs if a valid OpenIGTLink device is set if (m_IGTLDevice.IsNull()) return; this->SetNumberOfIndexedOutputs(1); if (this->GetOutput(0) == nullptr) { DataObjectPointer newOutput = this->MakeOutput(0); this->SetNthOutput(0, newOutput); this->Modified(); } } //void mitk::IGTLMessageProvider::UpdateOutputInformation() //{ // this->Modified(); // make sure that we need to be updated // Superclass::UpdateOutputInformation(); //} void mitk::IGTLMessageProvider::OnIncomingMessage() { } void mitk::IGTLMessageProvider::OnLostConnection() { //in case the provider is streaming at the moment we have to stop it if (m_IsStreaming) { MITK_DEBUG("IGTLMessageProvider") << "lost connection, stop streaming"; this->StopStreamingOfAllSources(); } } std::string RemoveRequestPrefixes(std::string requestType) { return requestType.substr(4); } void mitk::IGTLMessageProvider::OnIncomingCommand() { //get the next command igtl::MessageBase::Pointer curCommand = this->m_IGTLDevice->GetNextCommand(); //extract the type const char * requestType = curCommand->GetDeviceType(); //check the type std::string reqType(requestType); bool isGetMsg = !reqType.find("GET_"); bool isSTTMsg = !reqType.find("STT_"); bool isSTPMsg = !reqType.find("STP_"); //get the type from the request type (remove STT_, STP_, GET_, RTS_) std::string type = RemoveRequestPrefixes(requestType); //check all microservices if there is a fitting source for the requested type mitk::IGTLMessageSource::Pointer source = this->GetFittingSource(type.c_str()); //if there is no fitting source return a RTS message, if there is a RTS //type defined in the message factory send it if ( source.IsNull() ) { if ( !this->GetIGTLDevice()->SendRTSMessage(type.c_str()) ) { //sending RTS message failed, probably because the type is not in the //message factory MITK_WARN("IGTLMessageProvider") << "Tried to send a RTS message but did " "not succeed. Check if this type ( " << type << " ) was added to the message " "factory. "; } } else { if ( isGetMsg ) //if it is a single value push it into sending queue { //first it is necessary to update the source. This needs additional time //but is necessary. But are we really allowed to call this here? In which //thread are we? Is the source thread safe? source->Update(); mitk::IGTLMessage::Pointer sourceOutput = source->GetOutput(); if (sourceOutput.IsNotNull() && sourceOutput->IsDataValid()) { if ( source.IsNotNull() ) { this->GetIGTLDevice()->SendMessage(sourceOutput); } } } else if ( isSTTMsg ) { //read the requested frames per second int fps = 10; //read the fps from the command igtl::MessageBase* curCommandPt = curCommand.GetPointer(); if ( std::strcmp( curCommand->GetDeviceType(), "STT_BIND" ) == 0 ) { fps = ((igtl::StartBindMessage*)curCommandPt)->GetResolution(); } else if ( std::strcmp( curCommand->GetDeviceType(), "STT_QTDATA" ) == 0 ) { fps = ((igtl::StartQuaternionTrackingDataMessage*)curCommandPt)->GetResolution(); } else if ( std::strcmp( curCommand->GetDeviceType(), "STT_TDATA" ) == 0 ) { fps = ((igtl::StartTrackingDataMessage*)curCommandPt)->GetResolution(); } this->StartStreamingOfSource(source, fps); } else if ( isSTPMsg ) { this->StopStreamingOfSource(source); } else { //do nothing } } } bool mitk::IGTLMessageProvider::IsStreaming() { return m_IsStreaming; } void mitk::IGTLMessageProvider::StartStreamingOfSource(IGTLMessageSource* src, unsigned int fps) { if ( src == nullptr ) return; //so far the provider allows the streaming of a single source only //if the streaming thread is already running return a RTS message if ( !m_IsStreaming ) { //if it is a stream establish a connection between the provider and the //source this->ConnectTo(src); // calculate the streaming time - this->m_StreamingTimeMutex->Lock(); + this->m_StreamingTimeMutex.lock(); this->m_StreamingTime = 1.0 / (double) fps * 1000.0; - this->m_StreamingTimeMutex->Unlock(); + this->m_StreamingTimeMutex.unlock(); //// For streaming we need a continues time signal, since there is no timer //// available we start a thread that generates a timing signal //// This signal is invoked from the other thread the update of the pipeline //// has to be executed from the main thread. Thus, we use the //// callbackfromGUIThread class to pass the execution to the main thread //this->m_ThreadId = m_MultiThreader->SpawnThread(this->TimerThread, this); mitk::CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread( this->m_StreamingCommand); this->m_IsStreaming = true; } else { MITK_WARN("IGTLMessageProvider") << "This provider just supports the " "streaming of one source."; } } void mitk::IGTLMessageProvider::InvokeStartStreamingEvent() { this->InvokeEvent(StreamingStartRequiredEvent()); } void mitk::IGTLMessageProvider::InvokeStopStreamingEvent() { this->InvokeEvent(StreamingStopRequiredEvent()); } void mitk::IGTLMessageProvider::StopStreamingOfSource(IGTLMessageSource* src) { //this is something bad!!! The streaming thread has to be stopped before the //source is disconnected otherwise it can cause a crash. This has to be added!! this->DisconnectFrom(src); - //this->m_StopStreamingThreadMutex->Lock(); - //this->m_StopStreamingThread = true; - //this->m_StopStreamingThreadMutex->Unlock(); - mitk::CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread( this->m_StopStreamingCommand); //does this flag needs a mutex??? this->m_IsStreaming = false; } void mitk::IGTLMessageProvider::StopStreamingOfAllSources() { // \todo remove all inputs mitk::CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread( this->m_StopStreamingCommand); //does this flag needs a mutex??? this->m_IsStreaming = false; } mitk::IGTLMessageSource::Pointer mitk::IGTLMessageProvider::GetFittingSource(const char* requestedType) { //get the context us::ModuleContext* context = us::GetModuleContext(); //define the interface name std::string interface = mitk::IGTLMessageSource::US_INTERFACE_NAME; //specify a filter that defines the requested type std::string filter = "(" + mitk::IGTLMessageSource::US_PROPKEY_DEVICETYPE + "=" + requestedType + ")"; //find the fitting service std::vector serviceReferences = context->GetServiceReferences(interface, filter); //check if a service reference was found. It is also possible that several //services were found. This is not checked here, just the first one is taken. if ( serviceReferences.size() ) { mitk::IGTLMessageSource::Pointer curSource = context->GetService(serviceReferences.front()); if ( curSource.IsNotNull() ) return curSource; } //no service reference was found or found service reference has no valid source return nullptr; } void mitk::IGTLMessageProvider::Send(mitk::IGTLMessage::Pointer msg) { if (msg != nullptr) { MITK_INFO << "Sending OpenIGTLink Message: " << msg->ToString(); this->m_IGTLDevice->SendMessage(msg); } } void mitk::IGTLMessageProvider::ConnectTo( mitk::IGTLMessageSource* UpstreamFilter ) { for (DataObjectPointerArraySizeType i = 0; i < UpstreamFilter->GetNumberOfOutputs(); i++) { this->SetInput(i, UpstreamFilter->GetOutput(i)); } } void mitk::IGTLMessageProvider::DisconnectFrom( mitk::IGTLMessageSource* UpstreamFilter ) { if (UpstreamFilter == nullptr) return; for (DataObjectPointerArraySizeType i = 0; i < UpstreamFilter->GetNumberOfOutputs(); ++i) { auto input = UpstreamFilter->GetOutput(i); if (input == nullptr) continue; auto nb = this->GetNumberOfIndexedInputs(); for (DataObjectPointerArraySizeType i = 0; i < nb; ++i) { if (this->GetInput(i) == input) { this->RemoveInput(i); break; } } } } - -//ITK_THREAD_RETURN_TYPE mitk::IGTLMessageProvider::TimerThread(void* pInfoStruct) -//{ -// // extract this pointer from thread info structure -// struct itk::MultiThreader::ThreadInfoStruct * pInfo = -// (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; -// mitk::IGTLMessageProvider* thisObject = -// static_cast(pInfo->UserData); -// -// itk::SimpleMutexLock mutex; -// mutex.Lock(); -// -// thisObject->m_StopStreamingThreadMutex->Lock(); -// thisObject->m_StopStreamingThread = false; -// thisObject->m_StopStreamingThreadMutex->Unlock(); -// -// thisObject->m_StreamingTimeMutex->Lock(); -// unsigned int waitingTime = thisObject->m_StreamingTime; -// thisObject->m_StreamingTimeMutex->Unlock(); -// -// while (true) -// { -// thisObject->m_StopStreamingThreadMutex->Lock(); -// bool stopThread = thisObject->m_StopStreamingThread; -// thisObject->m_StopStreamingThreadMutex->Unlock(); -// -// if (stopThread) -// { -// break; -// } -// -// //wait for the time given -// //I know it is not the nicest solution but we just need an approximate time -// //sleeps for 20 ms -// #if defined (WIN32) || defined (_WIN32) -// Sleep(waitingTime); -// #else -// usleep(waitingTime * 1000); -// #endif -// -// // Ask to execute that command from the GUI thread -// mitk::CallbackFromGUIThread::GetInstance()->CallThisFromGUIThread( -// thisObject->m_StreamingCommand); -// } -// -// thisObject->m_ThreadId = 0; -// -// mutex.Unlock(); -// -// return ITK_THREAD_RETURN_VALUE; -//} diff --git a/Modules/OpenIGTLink/mitkIGTLMessageProvider.h b/Modules/OpenIGTLink/mitkIGTLMessageProvider.h index 1b158d0f56..17e0a9355a 100644 --- a/Modules/OpenIGTLink/mitkIGTLMessageProvider.h +++ b/Modules/OpenIGTLink/mitkIGTLMessageProvider.h @@ -1,221 +1,221 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 IGTLMESSAGEPROVIDER_H_HEADER_INCLUDED_ #define IGTLMESSAGEPROVIDER_H_HEADER_INCLUDED_ #include "mitkIGTLDevice.h" #include "mitkIGTLDeviceSource.h" //itk #include "itkCommand.h" namespace mitk { /** * \brief Provides information/objects from a MITK-Pipeline to other OpenIGTLink * devices * * This class is intended as the drain of the pipeline. Other OpenIGTLink * devices connect with the IGTLDevice hold by this provider. The other device * asks for a certain data type. The provider checks if there are other * IGTLMessageSources available that provide this data type. If yes the provider * connects with this source and sends the message to the requesting device. * * If a STT message was received the provider looks for fitting messageSources. * Once found it connects with it, starts a timing thread (which updates the * pipeline) and sends the result to the requesting device. * * If a GET message was received the provider just calls an update of the * found source and sends the result without connecting to the source. * * If a STP message was received it stops the thread and disconnects from the * previous source. * * So far the provider can just connect with one source. * * \ingroup OpenIGTLink */ class MITKOPENIGTLINK_EXPORT IGTLMessageProvider : public IGTLDeviceSource { public: mitkClassMacro(IGTLMessageProvider, IGTLDeviceSource); itkFactorylessNewMacro(Self); itkCloneMacro(Self); typedef itk::SimpleMemberCommand ProviderCommand; /** * \brief sends the msg to the requesting client * * Note: so far it broadcasts the message to all registered clients */ void Send(mitk::IGTLMessage::Pointer msg); /** * \brief Starts the streaming of the given message source with the given fps. */ void StartStreamingOfSource(mitk::IGTLMessageSource* src, unsigned int fps); /** * \brief Stops the streaming of the given message source. */ void StopStreamingOfSource(mitk::IGTLMessageSource* src); /** * \brief Stops the streaming of all message source. */ void StopStreamingOfAllSources(); /** * \brief Returns the streaming state. */ bool IsStreaming(); /** * \brief Get method for the streaming time */ itkGetMacro(StreamingTime, unsigned int); void Update() override; protected: IGTLMessageProvider(); ~IGTLMessageProvider() override; /** * \brief filter execute method * * queries the OpenIGTLink device for new messages and updates its output * igtl::MessageBase objects with it. * \warning Will raise a std::out_of_range exception, if tools were added to * the OpenIGTLink device after it was set as input for this filter */ void GenerateData() override; /** * \brief Create the necessary outputs for the m_IGTLDevice * * This Method is called internally whenever outputs need to be reset. Old * Outputs are deleted when called. **/ void CreateOutputs(); /** * \brief This method is called when the IGTL device hold by this class * receives a new message **/ void OnIncomingMessage() override; /** * \brief This method is called when the IGTL device hold by this class * receives a new command **/ void OnIncomingCommand() override; /** * \brief This method is called when the IGTL device lost the connection to the other side **/ void OnLostConnection() override; /** *\brief Connects the input of this filter to the outputs of the given * IGTLMessageSource * * This method does not support smartpointer. use FilterX.GetPointer() to * retrieve a dumbpointer. */ void ConnectTo( mitk::IGTLMessageSource* UpstreamFilter ); /** *\brief Disconnects this filter from the outputs of the given * IGTLMessageSource * * This method does not support smartpointer. use FilterX.GetPointer() to * retrieve a dumbpointer. */ void DisconnectFrom( mitk::IGTLMessageSource* UpstreamFilter ); /** * \brief Looks for microservices that provide messages with the requested * type. **/ mitk::IGTLMessageSource::Pointer GetFittingSource(const char* requestedType); /** Invokes the start streaming event. This separate method is required, because it * has to be started from the main thread. (It is used as callback function) */ void InvokeStartStreamingEvent(); /** Invokes the stop streaming event. This separate method is required, because it * has to be started from the main thread. (It is used as callback function) */ void InvokeStopStreamingEvent(); private: /** * \brief a command that has to be executed in the main thread */ ProviderCommand::Pointer m_StreamingCommand; ProviderCommand::Pointer m_StopStreamingCommand; ///** // * \brief Timer thread for generating a continuous time signal for the stream // * // * Everyt time the time is passed a time signal is invoked. // * // * \param pInfoStruct pointer to the mitkIGTLMessageProvider object // * \return // */ //static ITK_THREAD_RETURN_TYPE TimerThread(void* pInfoStruct); //int m_ThreadId; ///** \brief timer thread will terminate after the next wakeup if set to true */ //bool m_StopStreamingThread; //itk::SmartPointer m_MultiThreader; /** \brief the time used for streaming */ unsigned int m_StreamingTime; /** \brief mutex for guarding m_Time */ - itk::SmartPointer m_StreamingTimeMutex; + std::mutex m_StreamingTimeMutex; ///** \brief mutex for guarding m_StopStreamingThread */ //itk::SmartPointer m_StopStreamingThreadMutex; /** \brief flag to indicate if the provider is streaming */ bool m_IsStreaming; }; /** * \brief connect to this Event to get notified when a stream is requested * * \note It is necessary to do the following things to have streaming support: 1. listen to this * event. 2. When emitted start a timer with the given interval. 3. In the timeout method of * this timer call IGTLMessageProvider::Update. 4. Also listen to the StreamingStopRequiredEvent * and stop the timer imdediately. * */ - itkEventMacro(StreamingStartRequiredEvent, itk::AnyEvent); + itkEventMacroDeclaration(StreamingStartRequiredEvent, itk::AnyEvent); /** * \brief connect to this Event to get notified when a stream shall be stopped * * \note It is necessary to connect to this event and stop the streaming timer when called. * */ - itkEventMacro(StreamingStopRequiredEvent, itk::AnyEvent); + itkEventMacroDeclaration(StreamingStopRequiredEvent, itk::AnyEvent); } // namespace mitk #endif /* MITKIGTLMESSAGEPROVIDER_H_HEADER_INCLUDED_ */ diff --git a/Modules/OpenIGTLink/mitkIGTLMessageQueue.cpp b/Modules/OpenIGTLink/mitkIGTLMessageQueue.cpp index ff5e0ab256..72857ac7ce 100644 --- a/Modules/OpenIGTLink/mitkIGTLMessageQueue.cpp +++ b/Modules/OpenIGTLink/mitkIGTLMessageQueue.cpp @@ -1,308 +1,307 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkIGTLMessageQueue.h" #include #include "igtlMessageBase.h" void mitk::IGTLMessageQueue::PushSendMessage(mitk::IGTLMessage::Pointer message) { - this->m_Mutex->Lock(); + this->m_Mutex.lock(); if (this->m_BufferingType == IGTLMessageQueue::NoBuffering) m_SendQueue.clear(); m_SendQueue.push_back(message); - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); } void mitk::IGTLMessageQueue::PushCommandMessage(igtl::MessageBase::Pointer message) { - this->m_Mutex->Lock(); + this->m_Mutex.lock(); if (this->m_BufferingType == IGTLMessageQueue::NoBuffering) m_CommandQueue.clear(); m_CommandQueue.push_back(message); - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); } void mitk::IGTLMessageQueue::PushMessage(igtl::MessageBase::Pointer msg) { - this->m_Mutex->Lock(); + this->m_Mutex.lock(); std::stringstream infolog; infolog << "Received message of type "; if (dynamic_cast(msg.GetPointer()) != nullptr) { if (this->m_BufferingType == IGTLMessageQueue::NoBuffering) m_TrackingDataQueue.clear(); this->m_TrackingDataQueue.push_back(dynamic_cast(msg.GetPointer())); infolog << "TDATA"; } else if (dynamic_cast(msg.GetPointer()) != nullptr) { if (this->m_BufferingType == IGTLMessageQueue::NoBuffering) m_TransformQueue.clear(); this->m_TransformQueue.push_back(dynamic_cast(msg.GetPointer())); infolog << "TRANSFORM"; } else if (dynamic_cast(msg.GetPointer()) != nullptr) { if (this->m_BufferingType == IGTLMessageQueue::NoBuffering) m_StringQueue.clear(); this->m_StringQueue.push_back(dynamic_cast(msg.GetPointer())); infolog << "STRING"; } else if (dynamic_cast(msg.GetPointer()) != nullptr) { igtl::ImageMessage::Pointer imageMsg = dynamic_cast(msg.GetPointer()); int* dim = new int[3]; imageMsg->GetDimensions(dim); if (dim[2] > 1) { if (this->m_BufferingType == IGTLMessageQueue::NoBuffering) m_Image3dQueue.clear(); this->m_Image3dQueue.push_back(dynamic_cast(msg.GetPointer())); infolog << "IMAGE3D"; } else { if (this->m_BufferingType == IGTLMessageQueue::NoBuffering) m_Image2dQueue.clear(); this->m_Image2dQueue.push_back(dynamic_cast(msg.GetPointer())); infolog << "IMAGE2D"; } } else { if (this->m_BufferingType == IGTLMessageQueue::NoBuffering) m_MiscQueue.clear(); this->m_MiscQueue.push_back(msg); infolog << "OTHER"; } m_Latest_Message = msg; //MITK_INFO << infolog.str(); - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); } mitk::IGTLMessage::Pointer mitk::IGTLMessageQueue::PullSendMessage() { mitk::IGTLMessage::Pointer ret = nullptr; - this->m_Mutex->Lock(); + this->m_Mutex.lock(); if (this->m_SendQueue.size() > 0) { ret = this->m_SendQueue.front(); this->m_SendQueue.pop_front(); } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return ret; } igtl::MessageBase::Pointer mitk::IGTLMessageQueue::PullMiscMessage() { igtl::MessageBase::Pointer ret = nullptr; - this->m_Mutex->Lock(); + this->m_Mutex.lock(); if (this->m_MiscQueue.size() > 0) { ret = this->m_MiscQueue.front(); this->m_MiscQueue.pop_front(); } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return ret; } igtl::ImageMessage::Pointer mitk::IGTLMessageQueue::PullImage2dMessage() { igtl::ImageMessage::Pointer ret = nullptr; - this->m_Mutex->Lock(); + this->m_Mutex.lock(); if (this->m_Image2dQueue.size() > 0) { ret = this->m_Image2dQueue.front(); this->m_Image2dQueue.pop_front(); } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return ret; } igtl::ImageMessage::Pointer mitk::IGTLMessageQueue::PullImage3dMessage() { igtl::ImageMessage::Pointer ret = nullptr; - this->m_Mutex->Lock(); + this->m_Mutex.lock(); if (this->m_Image3dQueue.size() > 0) { ret = this->m_Image3dQueue.front(); this->m_Image3dQueue.pop_front(); } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return ret; } igtl::TrackingDataMessage::Pointer mitk::IGTLMessageQueue::PullTrackingMessage() { igtl::TrackingDataMessage::Pointer ret = nullptr; - this->m_Mutex->Lock(); + this->m_Mutex.lock(); if (this->m_TrackingDataQueue.size() > 0) { ret = this->m_TrackingDataQueue.front(); this->m_TrackingDataQueue.pop_front(); } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return ret; } igtl::MessageBase::Pointer mitk::IGTLMessageQueue::PullCommandMessage() { igtl::MessageBase::Pointer ret = nullptr; - this->m_Mutex->Lock(); + this->m_Mutex.lock(); if (this->m_CommandQueue.size() > 0) { ret = this->m_CommandQueue.front(); this->m_CommandQueue.pop_front(); } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return ret; } igtl::StringMessage::Pointer mitk::IGTLMessageQueue::PullStringMessage() { igtl::StringMessage::Pointer ret = nullptr; - this->m_Mutex->Lock(); + this->m_Mutex.lock(); if (this->m_StringQueue.size() > 0) { ret = this->m_StringQueue.front(); this->m_StringQueue.pop_front(); } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return ret; } igtl::TransformMessage::Pointer mitk::IGTLMessageQueue::PullTransformMessage() { igtl::TransformMessage::Pointer ret = nullptr; - this->m_Mutex->Lock(); + this->m_Mutex.lock(); if (this->m_TransformQueue.size() > 0) { ret = this->m_TransformQueue.front(); this->m_TransformQueue.pop_front(); } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return ret; } std::string mitk::IGTLMessageQueue::GetNextMsgInformationString() { - this->m_Mutex->Lock(); + this->m_Mutex.lock(); std::stringstream s; if (this->m_Latest_Message != nullptr) { s << "Device Type: " << this->m_Latest_Message->GetDeviceType() << std::endl; s << "Device Name: " << this->m_Latest_Message->GetDeviceName() << std::endl; } else { s << "No Msg"; } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return s.str(); } std::string mitk::IGTLMessageQueue::GetNextMsgDeviceType() { - this->m_Mutex->Lock(); + this->m_Mutex.lock(); std::stringstream s; if (m_Latest_Message != nullptr) { s << this->m_Latest_Message->GetDeviceType(); } else { s << ""; } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return s.str(); } std::string mitk::IGTLMessageQueue::GetLatestMsgInformationString() { - this->m_Mutex->Lock(); + this->m_Mutex.lock(); std::stringstream s; if (m_Latest_Message != nullptr) { s << "Device Type: " << this->m_Latest_Message->GetDeviceType() << std::endl; s << "Device Name: " << this->m_Latest_Message->GetDeviceName() << std::endl; } else { s << "No Msg"; } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return s.str(); } std::string mitk::IGTLMessageQueue::GetLatestMsgDeviceType() { - this->m_Mutex->Lock(); + this->m_Mutex.lock(); std::stringstream s; if (m_Latest_Message != nullptr) { s << this->m_Latest_Message->GetDeviceType(); } else { s << ""; } - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); return s.str(); } int mitk::IGTLMessageQueue::GetSize() { return (this->m_CommandQueue.size() + this->m_Image2dQueue.size() + this->m_Image3dQueue.size() + this->m_MiscQueue.size() + this->m_StringQueue.size() + this->m_TrackingDataQueue.size() + this->m_TransformQueue.size()); } void mitk::IGTLMessageQueue::EnableNoBufferingMode(bool enable) { - this->m_Mutex->Lock(); + this->m_Mutex.lock(); if (enable) this->m_BufferingType = IGTLMessageQueue::BufferingType::NoBuffering; else this->m_BufferingType = IGTLMessageQueue::BufferingType::Infinit; - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); } mitk::IGTLMessageQueue::IGTLMessageQueue() { - this->m_Mutex = itk::FastMutexLock::New(); this->m_BufferingType = IGTLMessageQueue::NoBuffering; } mitk::IGTLMessageQueue::~IGTLMessageQueue() { - this->m_Mutex->Unlock(); + this->m_Mutex.unlock(); } diff --git a/Modules/OpenIGTLink/mitkIGTLMessageQueue.h b/Modules/OpenIGTLink/mitkIGTLMessageQueue.h index 0863ecc23a..b33094565c 100644 --- a/Modules/OpenIGTLink/mitkIGTLMessageQueue.h +++ b/Modules/OpenIGTLink/mitkIGTLMessageQueue.h @@ -1,139 +1,139 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef IGTLMessageQueue_H #define IGTLMessageQueue_H #include "MitkOpenIGTLinkExports.h" #include "itkObject.h" -#include "itkFastMutexLock.h" #include "mitkCommon.h" #include +#include #include //OpenIGTLink #include "igtlMessageBase.h" #include "igtlImageMessage.h" #include "igtlStringMessage.h" #include "igtlTrackingDataMessage.h" #include "igtlTransformMessage.h" namespace mitk { /** * \class IGTLMessageQueue * \brief Thread safe message queue to store OpenIGTLink messages. * * \ingroup OpenIGTLink */ class MITKOPENIGTLINK_EXPORT IGTLMessageQueue : public itk::Object { public: mitkClassMacroItkParent(mitk::IGTLMessageQueue, itk::Object); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * \brief Different buffering types * Infinit buffering means that you can push as many messages as you want * NoBuffering means that the queue just stores a single message */ enum BufferingType { Infinit, NoBuffering }; void PushSendMessage(mitk::IGTLMessage::Pointer message); /** * \brief Adds the message to the queue */ void PushMessage(igtl::MessageBase::Pointer message); /** * \brief Adds the message to the queue */ void PushCommandMessage(igtl::MessageBase::Pointer message); /** * \brief Returns and removes the oldest message from the queue */ igtl::MessageBase::Pointer PullMiscMessage(); igtl::ImageMessage::Pointer PullImage2dMessage(); igtl::ImageMessage::Pointer PullImage3dMessage(); igtl::TrackingDataMessage::Pointer PullTrackingMessage(); igtl::MessageBase::Pointer PullCommandMessage(); igtl::StringMessage::Pointer PullStringMessage(); igtl::TransformMessage::Pointer PullTransformMessage(); mitk::IGTLMessage::Pointer PullSendMessage(); /** * \brief Get the number of messages in the queue */ int GetSize(); /** * \brief Returns a string with information about the oldest message in the * queue */ std::string GetNextMsgInformationString(); /** * \brief Returns the device type of the oldest message in the queue */ std::string GetNextMsgDeviceType(); /** * \brief Returns a string with information about the oldest message in the * queue */ std::string GetLatestMsgInformationString(); /** * \brief Returns the device type of the oldest message in the queue */ std::string GetLatestMsgDeviceType(); /** */ void EnableNoBufferingMode(bool enable); protected: IGTLMessageQueue(); ~IGTLMessageQueue() override; protected: /** * \brief Mutex to take car of the queue */ - itk::FastMutexLock::Pointer m_Mutex; + std::mutex m_Mutex; /** * \brief the queue that stores pointer to the inserted messages */ std::deque< igtl::MessageBase::Pointer > m_CommandQueue; std::deque< igtl::ImageMessage::Pointer > m_Image2dQueue; std::deque< igtl::ImageMessage::Pointer > m_Image3dQueue; std::deque< igtl::TransformMessage::Pointer > m_TransformQueue; std::deque< igtl::TrackingDataMessage::Pointer > m_TrackingDataQueue; std::deque< igtl::StringMessage::Pointer > m_StringQueue; std::deque< igtl::MessageBase::Pointer > m_MiscQueue; std::deque< mitk::IGTLMessage::Pointer > m_SendQueue; igtl::MessageBase::Pointer m_Latest_Message; /** * \brief defines the kind of buffering */ BufferingType m_BufferingType; }; } #endif diff --git a/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp b/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp index f42dedd4dd..37c3d29deb 100644 --- a/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp +++ b/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp @@ -1,198 +1,197 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkIGTLMessageSource.h" #include "mitkUIDGenerator.h" //Microservices #include #include #include #include const std::string mitk::IGTLMessageSource::US_INTERFACE_NAME = "org.mitk.services.IGTLMessageSource"; const std::string mitk::IGTLMessageSource::US_PROPKEY_DEVICENAME = US_INTERFACE_NAME + ".devicename"; const std::string mitk::IGTLMessageSource::US_PROPKEY_DEVICETYPE = US_INTERFACE_NAME + ".devicetype"; const std::string mitk::IGTLMessageSource::US_PROPKEY_ID = US_INTERFACE_NAME + ".id"; const std::string mitk::IGTLMessageSource::US_PROPKEY_ISACTIVE = US_INTERFACE_NAME + ".isActive"; mitk::IGTLMessageSource::IGTLMessageSource() : itk::ProcessObject(), m_Name("IGTLMessageSource (no defined type)"), m_Type("NONE"), m_StreamingFPS(0) { - m_StreamingFPSMutex = itk::FastMutexLock::New(); } mitk::IGTLMessageSource::~IGTLMessageSource() { //this->UnRegisterMicroservice(); } mitk::IGTLMessage* mitk::IGTLMessageSource::GetOutput() { if (this->GetNumberOfIndexedOutputs() < 1) { MITK_WARN << "IGTLMessageSource contained no outputs. Returning nullptr."; return nullptr; } return static_cast(this->ProcessObject::GetPrimaryOutput()); } mitk::IGTLMessage* mitk::IGTLMessageSource::GetOutput( DataObjectPointerArraySizeType idx) { IGTLMessage* out = dynamic_cast( this->ProcessObject::GetOutput(idx) ); if ( out == nullptr && this->ProcessObject::GetOutput(idx) != nullptr ) { itkWarningMacro (<< "Unable to convert output number " << idx << " to type " << typeid( IGTLMessage ).name () ); } return out; } mitk::IGTLMessage* mitk::IGTLMessageSource::GetOutput( const std::string& messageName) { DataObjectPointerArray outputs = this->GetOutputs(); for (DataObjectPointerArray::iterator it = outputs.begin(); it != outputs.end(); ++it) { if (messageName == (static_cast(it->GetPointer()))->GetName()) { return static_cast(it->GetPointer()); } } return nullptr; } itk::ProcessObject::DataObjectPointerArraySizeType mitk::IGTLMessageSource::GetOutputIndex( std::string messageName ) { DataObjectPointerArray outputs = this->GetOutputs(); for (DataObjectPointerArray::size_type i = 0; i < outputs.size(); ++i) { if (messageName == (static_cast(outputs.at(i).GetPointer()))->GetName()) { return i; } } throw std::invalid_argument("output name does not exist"); } void mitk::IGTLMessageSource::RegisterAsMicroservice() { // Get Context us::ModuleContext* context = us::GetModuleContext(); // Define ServiceProps us::ServiceProperties props; mitk::UIDGenerator uidGen = mitk::UIDGenerator ("org.mitk.services.IGTLMessageSource.id_"); props[ US_PROPKEY_ID ] = uidGen.GetUID(); props[ US_PROPKEY_DEVICENAME ] = m_Name; props[ US_PROPKEY_DEVICETYPE ] = m_Type; m_ServiceRegistration = context->RegisterService(this, props); } void mitk::IGTLMessageSource::UnRegisterMicroservice() { if (m_ServiceRegistration != nullptr) { m_ServiceRegistration.Unregister(); } m_ServiceRegistration = 0; } std::string mitk::IGTLMessageSource::GetMicroserviceID() { us::Any referenceProperty = this->m_ServiceRegistration.GetReference().GetProperty(US_PROPKEY_ID); return referenceProperty.ToString(); } void mitk::IGTLMessageSource::GraftOutput(itk::DataObject *graft) { this->GraftNthOutput(0, graft); } void mitk::IGTLMessageSource::GraftNthOutput(unsigned int idx, itk::DataObject *graft) { if ( idx >= this->GetNumberOfIndexedOutputs() ) { itkExceptionMacro(<<"Requested to graft output " << idx << " but this filter" "only has " << this->GetNumberOfIndexedOutputs() << " Outputs."); } if ( !graft ) { itkExceptionMacro(<<"Requested to graft output with a nullptr pointer object" ); } itk::DataObject* output = this->GetOutput(idx); if ( !output ) { itkExceptionMacro(<<"Requested to graft output that is a nullptr pointer" ); } // Call Graft on IGTLMessage to copy member data output->Graft( graft ); } itk::DataObject::Pointer mitk::IGTLMessageSource::MakeOutput ( DataObjectPointerArraySizeType /*idx*/ ) { return IGTLMessage::New().GetPointer(); } itk::DataObject::Pointer mitk::IGTLMessageSource::MakeOutput( const DataObjectIdentifierType & name ) { itkDebugMacro("MakeOutput(" << name << ")"); if( this->IsIndexedOutputName(name) ) { return this->MakeOutput( this->MakeIndexFromOutputName(name) ); } return static_cast(IGTLMessage::New().GetPointer()); } mitk::PropertyList::ConstPointer mitk::IGTLMessageSource::GetParameters() const { mitk::PropertyList::Pointer p = mitk::PropertyList::New(); // add properties to p like this: //p->SetProperty("MyFilter_MyParameter", mitk::PropertyDataType::New(m_MyParameter)); return mitk::PropertyList::ConstPointer(p); } void mitk::IGTLMessageSource::SetFPS(unsigned int fps) { - this->m_StreamingFPSMutex->Lock(); + this->m_StreamingFPSMutex.lock(); this->m_StreamingFPS = fps; - this->m_StreamingFPSMutex->Unlock(); + this->m_StreamingFPSMutex.unlock(); } unsigned int mitk::IGTLMessageSource::GetFPS() { unsigned int fps = 0; - this->m_StreamingFPSMutex->Lock(); + this->m_StreamingFPSMutex.lock(); fps = this->m_StreamingFPS; - this->m_StreamingFPSMutex->Unlock(); + this->m_StreamingFPSMutex.unlock(); return fps; } diff --git a/Modules/OpenIGTLink/mitkIGTLMessageSource.h b/Modules/OpenIGTLink/mitkIGTLMessageSource.h index 2c3e4ac29e..320c66b005 100644 --- a/Modules/OpenIGTLink/mitkIGTLMessageSource.h +++ b/Modules/OpenIGTLink/mitkIGTLMessageSource.h @@ -1,208 +1,207 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKIGTLMESSAGESOURCE_H_HEADER_INCLUDED_ #define MITKIGTLMESSAGESOURCE_H_HEADER_INCLUDED_ #include #include "mitkPropertyList.h" #include "MitkOpenIGTLinkExports.h" #include "mitkIGTLMessage.h" // Microservices #include #include -//itk -#include +#include namespace mitk { /** * \brief OpenIGTLink message source * * Base class for all OpenIGTLink filters that produce OpenIGTLink message * objects as output. This class defines the output-interface for * OpenIGTLinkMessageFilters. * \warning: if Update() is called on any output object, all IGTLMessage filters * will generate new output data for all outputs, not just the one on which * Update() was called. * */ class MITKOPENIGTLINK_EXPORT IGTLMessageSource : public itk::ProcessObject { public: mitkClassMacroItkParent(IGTLMessageSource, itk::ProcessObject); /** @return Returns a human readable name of this source. There will be a * default name, or you can set the name with the method SetName() if you * want to change it. */ itkGetMacro(Name,std::string); /** @brief Sets the human readable name of this source. There is also a * default name, but you can use this method if you need to define it on your * own. */ itkSetMacro(Name,std::string); /** @return Returns a human readable type of this source. There will be a * default type, or you can set the name with the method SetType(). You have * to set this parameter otherwise it will not be found by the message * provider. */ itkGetMacro(Type,std::string); /** @return Returns a human readable type of this source. There will be a * default type, or you can set the name with the method SetType(). You have * to set this parameter otherwise it will not be found by the message * provider. */ itkSetMacro(Type,std::string); /** *\brief return the output (output with id 0) of the filter */ IGTLMessage* GetOutput(void); /** *\brief return the output with id idx of the filter */ IGTLMessage* GetOutput(DataObjectPointerArraySizeType idx); /** *\brief return the output with name messageName of the filter */ IGTLMessage* GetOutput(const std::string& messageName); /** *\brief return the index of the output with name messageName, -1 if no output * with that name was found * * \warning if a subclass has outputs that have different data type than * igtl::MessageBase, they have to overwrite this method */ DataObjectPointerArraySizeType GetOutputIndex(std::string messageName); /** *\brief Registers this object as a Microservice, making it available to every * module and/or plugin. To unregister, call UnregisterMicroservice(). */ virtual void RegisterAsMicroservice(); /** *\brief Registers this object as a Microservice, making it available to every * module and/or plugin. */ virtual void UnRegisterMicroservice(); /** *\brief Returns the id that this device is registered with. The id will only * be valid, if the IGTLMessageSource has been registered using * RegisterAsMicroservice(). */ std::string GetMicroserviceID(); /** *\brief These Constants are used in conjunction with Microservices */ static const std::string US_INTERFACE_NAME; static const std::string US_PROPKEY_DEVICENAME; static const std::string US_PROPKEY_DEVICETYPE; static const std::string US_PROPKEY_ID; static const std::string US_PROPKEY_ISACTIVE; //NOT IMPLEMENTED YET! /** *\brief Graft the specified DataObject onto this ProcessObject's output. * * See itk::ImageSource::GraftNthOutput for details */ virtual void GraftNthOutput(unsigned int idx, itk::DataObject *graft); /** * \brief Graft the specified DataObject onto this ProcessObject's output. * * See itk::ImageSource::Graft Output for details */ virtual void GraftOutput(itk::DataObject *graft); /** * Allocates a new output object and returns it. Currently the * index idx is not evaluated. * @param idx the index of the output for which an object should be created * @returns the new object */ itk::DataObject::Pointer MakeOutput ( DataObjectPointerArraySizeType idx ) override; /** * This is a default implementation to make sure we have something. * Once all the subclasses of ProcessObject provide an appopriate * MakeOutput(), then ProcessObject::MakeOutput() can be made pure * virtual. */ itk::DataObject::Pointer MakeOutput(const DataObjectIdentifierType &name) override; /** * \brief Set all filter parameters as the PropertyList p * * This method allows to set all parameters of a filter with one * method call. For the names of the parameters, take a look at * the GetParameters method of the filter * This method has to be overwritten by each MITK-IGT filter. */ virtual void SetParameters(const mitk::PropertyList*){}; /** * \brief Get all filter parameters as a PropertyList * * This method allows to get all parameters of a filter with one * method call. The returned PropertyList must be assigned to a * SmartPointer immediately, or else it will get destroyed. * Every filter must overwrite this method to create a filter-specific * PropertyList. Note that property names must be unique over all * MITK-IGT filters. Therefore each filter should use its name as a prefix * for each property name. * Secondly, each filter should list the property names and data types * in the method documentation. */ virtual mitk::PropertyList::ConstPointer GetParameters() const; /** *\brief Sets the fps used for streaming this source */ void SetFPS(unsigned int fps); /** *\brief Gets the fps used for streaming this source */ unsigned int GetFPS(); protected: IGTLMessageSource(); ~IGTLMessageSource() override; std::string m_Name; std::string m_Type; /** mutex to control access to m_StreamingFPS */ - itk::FastMutexLock::Pointer m_StreamingFPSMutex; + std::mutex m_StreamingFPSMutex; /** The frames per second used for streaming */ unsigned int m_StreamingFPS; us::ServiceRegistration m_ServiceRegistration; }; } // namespace mitk // This is the microservice declaration. Do not meddle! MITK_DECLARE_SERVICE_INTERFACE(mitk::IGTLMessageSource, "org.mitk.services.IGTLMessageSource") #endif /* MITKIGTLMESSAGESOURCE_H_HEADER_INCLUDED_ */ diff --git a/Modules/OpenIGTLink/mitkIGTLServer.cpp b/Modules/OpenIGTLink/mitkIGTLServer.cpp index 817b37c3e3..e18c9ab5be 100644 --- a/Modules/OpenIGTLink/mitkIGTLServer.cpp +++ b/Modules/OpenIGTLink/mitkIGTLServer.cpp @@ -1,212 +1,207 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkIGTLServer.h" #include #include -#include #include #include #include #include mitk::IGTLServer::IGTLServer(bool ReadFully) : IGTLDevice(ReadFully) { - m_ReceiveListMutex = itk::FastMutexLock::New(); - m_SentListMutex = itk::FastMutexLock::New(); } mitk::IGTLServer::~IGTLServer() { - m_ReceiveListMutex = nullptr; - m_SentListMutex = nullptr; } bool mitk::IGTLServer::OpenConnection() { if (this->GetState() != Setup) { mitkThrowException(mitk::Exception) << "Can only try to create a server if in setup mode"; return false; } int portNumber = this->GetPortNumber(); if (portNumber == -1) { //port number was not correct return false; } //create a new server socket m_Socket = igtl::ServerSocket::New(); //try to create the igtl server int response = dynamic_cast(m_Socket.GetPointer())-> CreateServer(portNumber); //check the response if (response != 0) { mitkThrowException(mitk::Exception) << "The server could not be created. Port: " << portNumber; return false; } // everything is initialized and connected so the communication can be started this->SetState(Ready); return true; } bool mitk::IGTLServer::CloseConnection() { //remove all registered clients - m_SentListMutex->Lock(); - m_ReceiveListMutex->Lock(); + m_SentListMutex.lock(); + m_ReceiveListMutex.lock(); SocketListType allRegisteredSockets(m_RegisteredClients); - m_SentListMutex->Unlock(); - m_ReceiveListMutex->Unlock(); + m_SentListMutex.unlock(); + m_ReceiveListMutex.unlock(); this->StopCommunicationWithSocket(allRegisteredSockets); return mitk::IGTLDevice::CloseConnection(); } void mitk::IGTLServer::Connect() { igtl::Socket::Pointer socket; //check if another igtl device wants to connect to this socket socket = ((igtl::ServerSocket*)(this->m_Socket.GetPointer()))->WaitForConnection(1); //if there is a new connection the socket is not null if (socket.IsNotNull()) { //add the new client socket to the list of registered clients - m_SentListMutex->Lock(); - m_ReceiveListMutex->Lock(); + m_SentListMutex.lock(); + m_ReceiveListMutex.lock(); this->m_RegisteredClients.push_back(socket); - m_SentListMutex->Unlock(); - m_ReceiveListMutex->Unlock(); + m_SentListMutex.unlock(); + m_ReceiveListMutex.unlock(); //inform observers about this new client this->InvokeEvent(NewClientConnectionEvent()); MITK_INFO("IGTLServer") << "Connected to a new client: " << socket; } } void mitk::IGTLServer::Receive() { unsigned int status = IGTL_STATUS_OK; SocketListType socketsToBeRemoved; //the server can be connected with several clients, therefore it has to check //all registered clients SocketListIteratorType it; - m_ReceiveListMutex->Lock(); + m_ReceiveListMutex.lock(); auto it_end = this->m_RegisteredClients.end(); for (it = this->m_RegisteredClients.begin(); it != it_end; ++it) { //it is possible that ReceivePrivate detects that the current socket is //already disconnected. Therefore, it is necessary to remove this socket //from the registered clients list status = this->ReceivePrivate(*it); if (status == IGTL_STATUS_NOT_PRESENT) { //remember this socket for later, it is not a good idea to remove it //from the list directly because we iterate over the list at this point socketsToBeRemoved.push_back(*it); MITK_WARN("IGTLServer") << "Lost connection to a client socket. "; } else if (status != 1) { MITK_DEBUG("IGTLServer") << "IGTL Message with status: " << status; } } - m_ReceiveListMutex->Unlock(); + m_ReceiveListMutex.unlock(); if (socketsToBeRemoved.size() > 0) { //remove the sockets that are not connected anymore this->StopCommunicationWithSocket(socketsToBeRemoved); //inform observers about loosing the connection to these sockets this->InvokeEvent(LostConnectionEvent()); } } void mitk::IGTLServer::Send() { //get the latest message from the queue mitk::IGTLMessage::Pointer curMessage = this->m_MessageQueue->PullSendMessage(); // there is no message => return if (curMessage.IsNull()) return; //the server can be connected with several clients, therefore it has to check //all registered clients //sending a message to all registered clients might not be the best solution, //it could be better to store the client together with the requested type. Then //the data would be send to the appropriate client and to noone else. //(I know it is no excuse but PLUS is doing exactly the same, they broadcast //everything) - m_SentListMutex->Lock(); + m_SentListMutex.lock(); SocketListIteratorType it; auto it_end = this->m_RegisteredClients.end(); for (it = this->m_RegisteredClients.begin(); it != it_end; ++it) { //maybe there should be a check here if the current socket is still active this->SendMessagePrivate(curMessage, *it); MITK_DEBUG("IGTLServer") << "Sent IGTL Message"; } - m_SentListMutex->Unlock(); + m_SentListMutex.unlock(); } void mitk::IGTLServer::StopCommunicationWithSocket( SocketListType& toBeRemovedSockets) { for (auto i = toBeRemovedSockets.begin(); i != toBeRemovedSockets.end(); i++) this->StopCommunicationWithSocket(*i); } void mitk::IGTLServer::StopCommunicationWithSocket(igtl::Socket* client) { - m_SentListMutex->Lock(); - m_ReceiveListMutex->Lock(); + m_SentListMutex.lock(); + m_ReceiveListMutex.lock(); auto i = m_RegisteredClients.begin(); auto end = m_RegisteredClients.end(); while (i != end) { if ((*i) == client) { // //close the socket (*i)->CloseSocket(); //and remove it from the list i = this->m_RegisteredClients.erase(i); MITK_INFO("IGTLServer") << "Removed client socket from server client list."; break; } else { ++i; } } - m_SentListMutex->Unlock(); - m_ReceiveListMutex->Unlock(); + m_SentListMutex.unlock(); + m_ReceiveListMutex.unlock(); } unsigned int mitk::IGTLServer::GetNumberOfConnections() { return this->m_RegisteredClients.size(); } diff --git a/Modules/OpenIGTLink/mitkIGTLServer.h b/Modules/OpenIGTLink/mitkIGTLServer.h index 62a085d328..75b4608273 100644 --- a/Modules/OpenIGTLink/mitkIGTLServer.h +++ b/Modules/OpenIGTLink/mitkIGTLServer.h @@ -1,124 +1,124 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKIGTLSERVER_H #define MITKIGTLSERVER_H #include "mitkIGTLDevice.h" #include namespace mitk { /** * \brief Superclass for OpenIGTLink server * * Implements the IGTLDevice interface for IGTLServers. In certain points it * behaves different than the IGTLClient. The client connects directly to a * server (it cannot connect to two different servers) while the server can * connect to several clients. Therefore, it is necessary for the server to * have a list with registered sockets. * * \ingroup OpenIGTLink */ class MITKOPENIGTLINK_EXPORT IGTLServer : public IGTLDevice { public: mitkClassMacro(IGTLServer, IGTLDevice); mitkNewMacro1Param(Self, bool); itkCloneMacro(Self); typedef std::list SocketListType; typedef SocketListType::iterator SocketListIteratorType; /** * \brief Initialize the connection for the IGTLServer * * * OpenConnection() starts the IGTLServer socket so that clients can connect * to it. * @throw mitk::Exception Throws an exception if the given port is occupied. */ bool OpenConnection() override; /** * \brief Closes the connection to the device * * This may only be called if there is currently a connection to the * device, but device is not running (e.g. object is in Ready state) */ bool CloseConnection() override; /** * \brief Returns the number of client connections of this device */ unsigned int GetNumberOfConnections() override; protected: /** Constructor */ IGTLServer(bool ReadFully); /** Destructor */ ~IGTLServer() override; /** * \brief Call this method to check for other devices that want to connect * to this one. * * In case of a client this method is doing nothing. In case of a server it * is checking for other devices and if there is one it establishes a * connection and adds the socket to m_RegisteredClients. */ void Connect() override; /** * \brief Call this method to receive a message. * * The message will be saved in the receive queue. */ void Receive() override; /** * \brief Call this method to send a message. * The message will be read from the queue. So far the message is send to all * connected sockets (broadcast). */ void Send() override; /** * \brief Stops the communication with the given sockets. * * This method removes the given sockets from the registered clients list * */ virtual void StopCommunicationWithSocket(SocketListType& toBeRemovedSockets); /** * \brief Stops the communication with the given socket. * * This method removes the given socket from the registered clients list * */ void StopCommunicationWithSocket(igtl::Socket* client) override; /** * \brief A list with all registered clients */ SocketListType m_RegisteredClients; /** mutex to control access to m_RegisteredClients */ - itk::FastMutexLock::Pointer m_ReceiveListMutex; + std::mutex m_ReceiveListMutex; /** mutex to control access to m_RegisteredClients */ - itk::FastMutexLock::Pointer m_SentListMutex; + std::mutex m_SentListMutex; }; } // namespace mitk #endif /* MITKIGTLSERVER_H */ diff --git a/Modules/PlanarFigure/include/mitkPlanarFigureInteractor.h b/Modules/PlanarFigure/include/mitkPlanarFigureInteractor.h index 63a3f33642..84a3f8892a 100644 --- a/Modules/PlanarFigure/include/mitkPlanarFigureInteractor.h +++ b/Modules/PlanarFigure/include/mitkPlanarFigureInteractor.h @@ -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. ============================================================================*/ #ifndef MITKPLANARFIGUREINTERACTOR_H #define MITKPLANARFIGUREINTERACTOR_H #include #include "mitkCommon.h" #include "mitkDataInteractor.h" #include "mitkNumericTypes.h" #pragma GCC visibility push(default) #include #pragma GCC visibility pop namespace mitk { class DataNode; class PlaneGeometry; class PlanarFigure; class PositionEvent; class BaseRenderer; class InteractionPositionEvent; class StateMachineAction; #pragma GCC visibility push(default) // Define events for PlanarFigure interaction notifications - itkEventMacro(PlanarFigureEvent, itk::AnyEvent); - itkEventMacro(StartPlacementPlanarFigureEvent, PlanarFigureEvent); - itkEventMacro(EndPlacementPlanarFigureEvent, PlanarFigureEvent); - itkEventMacro(SelectPlanarFigureEvent, PlanarFigureEvent); - itkEventMacro(StartInteractionPlanarFigureEvent, PlanarFigureEvent); - itkEventMacro(EndInteractionPlanarFigureEvent, PlanarFigureEvent); - itkEventMacro(StartHoverPlanarFigureEvent, PlanarFigureEvent); - itkEventMacro(EndHoverPlanarFigureEvent, PlanarFigureEvent); - itkEventMacro(ContextMenuPlanarFigureEvent, PlanarFigureEvent); - itkEventMacro(PointMovedPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDeclaration(PlanarFigureEvent, itk::AnyEvent); + itkEventMacroDeclaration(StartPlacementPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDeclaration(EndPlacementPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDeclaration(SelectPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDeclaration(StartInteractionPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDeclaration(EndInteractionPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDeclaration(StartHoverPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDeclaration(EndHoverPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDeclaration(ContextMenuPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDeclaration(PointMovedPlanarFigureEvent, PlanarFigureEvent); #pragma GCC visibility pop /** * \brief Interaction with mitk::PlanarFigure objects via control-points * * @ingroup MitkPlanarFigureModule */ class MITKPLANARFIGURE_EXPORT PlanarFigureInteractor : public DataInteractor { public: mitkClassMacro(PlanarFigureInteractor, DataInteractor); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** \brief Sets the amount of precision */ void SetPrecision(ScalarType precision); /** \brief Sets the minimal distance between two control points. */ void SetMinimumPointDistance(ScalarType minimumDistance); protected: PlanarFigureInteractor(); ~PlanarFigureInteractor() override; void ConnectActionsAndFunctions() override; //////// Conditions //////// bool CheckFigurePlaced(const InteractionEvent *interactionEvent); bool CheckFigureHovering(const InteractionEvent *interactionEvent); bool CheckControlPointHovering(const InteractionEvent *interactionEvent); bool CheckSelection(const InteractionEvent *interactionEvent); bool CheckPointValidity(const InteractionEvent *interactionEvent); bool CheckFigureFinished(const InteractionEvent *interactionEvent); bool CheckResetOnPointSelect(const InteractionEvent *interactionEvent); bool CheckFigureOnRenderingGeometry(const InteractionEvent *interactionEvent); bool CheckMinimalFigureFinished(const InteractionEvent *interactionEvent); bool CheckFigureIsExtendable(const InteractionEvent *interactionEvent); bool CheckFigureIsDeletable(const InteractionEvent *interactionEvent); bool CheckFigureIsEditable(const InteractionEvent *interactionEvent); //////// Actions //////// void FinalizeFigure(StateMachineAction *, InteractionEvent *interactionEvent); void MoveCurrentPoint(StateMachineAction *, InteractionEvent *interactionEvent); void DeselectPoint(StateMachineAction *, InteractionEvent *interactionEvent); void AddPoint(StateMachineAction *, InteractionEvent *interactionEvent); void AddInitialPoint(StateMachineAction *, InteractionEvent *interactionEvent); void StartHovering(StateMachineAction *, InteractionEvent *interactionEvent); void EndHovering(StateMachineAction *, InteractionEvent *interactionEvent); void DeleteFigure(StateMachineAction *, InteractionEvent *interactionEvent); void PerformPointResetOnSelect(StateMachineAction *, InteractionEvent *interactionEvent); void SetPreviewPointPosition(StateMachineAction *, InteractionEvent *interactionEvent); void HidePreviewPoint(StateMachineAction *, InteractionEvent *interactionEvent); void HideControlPoints(StateMachineAction *, InteractionEvent *interactionEvent); void RemoveSelectedPoint(StateMachineAction *, InteractionEvent *interactionEvent); void RequestContextMenu(StateMachineAction *, InteractionEvent *interactionEvent); void SelectFigure(StateMachineAction *, InteractionEvent *interactionEvent); void SelectPoint(StateMachineAction *, InteractionEvent *interactionEvent); void EndInteraction(StateMachineAction *, InteractionEvent *interactionEvent); bool FilterEvents(InteractionEvent *interactionEvent, DataNode *) override; /** \brief Used when clicking to determine if a point is too close to the previous point. */ bool IsMousePositionAcceptableAsNewControlPoint(const mitk::InteractionPositionEvent *positionEvent, const PlanarFigure *); bool TransformPositionEventToPoint2D(const InteractionPositionEvent *positionEvent, const PlaneGeometry *planarFigureGeometry, Point2D &point2D); bool TransformObjectToDisplay(const mitk::Point2D &point2D, mitk::Point2D &displayPoint, const mitk::PlaneGeometry *objectGeometry, const mitk::PlaneGeometry *rendererGeometry, const mitk::BaseRenderer *renderer) const; /** \brief Returns true if the first specified point is in proximity of the line defined * the other two point; false otherwise. * * Proximity is defined as the rectangle around the line with pre-defined distance * from the line. */ bool IsPointNearLine(const mitk::Point2D &point, const mitk::Point2D &startPoint, const mitk::Point2D &endPoint, mitk::Point2D &projectedPoint) const; /** \brief Returns true if the point contained in the passed event (in display coordinates) * is over the planar figure (with a pre-defined tolerance range); false otherwise. */ int IsPositionOverFigure(const InteractionPositionEvent *positionEvent, PlanarFigure *planarFigure, const PlaneGeometry *planarFigureGeometry, const PlaneGeometry *rendererGeometry, Point2D &pointProjectedOntoLine) const; /** \brief Returns the index of the marker (control point) over which the point contained * in the passed event (in display coordinates) currently is; -1 if the point is not over * a marker. */ int IsPositionInsideMarker(const InteractionPositionEvent *positionEvent, const PlanarFigure *planarFigure, const PlaneGeometry *planarFigureGeometry, const PlaneGeometry *rendererGeometry, const BaseRenderer *renderer) const; void LogPrintPlanarFigureQuantities(const PlanarFigure *planarFigure); void ConfigurationChanged() override; private: /** \brief to store the value of precision to pick a point */ ScalarType m_Precision; /** \brief Store the minimal distance between two control points. */ ScalarType m_MinimumPointDistance; /** \brief True if the mouse is currently hovering over the image. */ bool m_IsHovering; }; } #endif // MITKPLANARFIGUREINTERACTOR_H diff --git a/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp b/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp index 706ac0b24b..9556791b3f 100644 --- a/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp +++ b/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp @@ -1,1115 +1,1129 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 PLANARFIGUREINTERACTOR_DBG MITK_DEBUG("PlanarFigureInteractor") << __LINE__ << ": " #include "mitkPlanarFigureInteractor.h" #include "mitkPlanarBezierCurve.h" #include "mitkPlanarCircle.h" #include "mitkPlanarFigure.h" #include "mitkPlanarPolygon.h" #include "mitkInteractionPositionEvent.h" #include "mitkInternalEvent.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkAbstractTransformGeometry.h" #include "mitkPlaneGeometry.h" +namespace mitk +{ + itkEventMacroDefinition(PlanarFigureEvent, itk::AnyEvent); + itkEventMacroDefinition(StartPlacementPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDefinition(EndPlacementPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDefinition(SelectPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDefinition(StartInteractionPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDefinition(EndInteractionPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDefinition(StartHoverPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDefinition(EndHoverPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDefinition(ContextMenuPlanarFigureEvent, PlanarFigureEvent); + itkEventMacroDefinition(PointMovedPlanarFigureEvent, PlanarFigureEvent); +} + mitk::PlanarFigureInteractor::PlanarFigureInteractor() : DataInteractor() , m_Precision(6.5) , m_MinimumPointDistance(25.0) , m_IsHovering(false) { } mitk::PlanarFigureInteractor::~PlanarFigureInteractor() { } void mitk::PlanarFigureInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("figure_is_on_current_slice", CheckFigureOnRenderingGeometry); CONNECT_CONDITION("figure_is_placed", CheckFigurePlaced); CONNECT_CONDITION("minimal_figure_is_finished", CheckMinimalFigureFinished); CONNECT_CONDITION("hovering_above_figure", CheckFigureHovering); CONNECT_CONDITION("hovering_above_point", CheckControlPointHovering); CONNECT_CONDITION("figure_is_selected", CheckSelection); CONNECT_CONDITION("point_is_valid", CheckPointValidity); CONNECT_CONDITION("figure_is_finished", CheckFigureFinished); CONNECT_CONDITION("reset_on_point_select_needed", CheckResetOnPointSelect); CONNECT_CONDITION("points_can_be_added_or_removed", CheckFigureIsExtendable); CONNECT_CONDITION("figure_can_be_deleted", CheckFigureIsDeletable); CONNECT_CONDITION("figure_is_editable", CheckFigureIsEditable); CONNECT_FUNCTION("finalize_figure", FinalizeFigure); CONNECT_FUNCTION("hide_preview_point", HidePreviewPoint) CONNECT_FUNCTION("hide_control_points", HideControlPoints) CONNECT_FUNCTION("set_preview_point_position", SetPreviewPointPosition) CONNECT_FUNCTION("move_current_point", MoveCurrentPoint); CONNECT_FUNCTION("deselect_point", DeselectPoint); CONNECT_FUNCTION("add_new_point", AddPoint); CONNECT_FUNCTION("add_initial_point", AddInitialPoint); CONNECT_FUNCTION("remove_selected_point", RemoveSelectedPoint); CONNECT_FUNCTION("request_context_menu", RequestContextMenu); CONNECT_FUNCTION("select_figure", SelectFigure); CONNECT_FUNCTION("select_point", SelectPoint); CONNECT_FUNCTION("end_interaction", EndInteraction); CONNECT_FUNCTION("start_hovering", StartHovering) CONNECT_FUNCTION("end_hovering", EndHovering); CONNECT_FUNCTION("delete_figure", DeleteFigure); CONNECT_FUNCTION("reset_on_point_select", PerformPointResetOnSelect); } bool mitk::PlanarFigureInteractor::CheckFigurePlaced(const InteractionEvent * /*interactionEvent*/) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return false; } bool isFigureFinished = false; planarFigure->GetPropertyList()->GetBoolProperty("initiallyplaced", isFigureFinished); return planarFigure->IsPlaced() && isFigureFinished; } void mitk::PlanarFigureInteractor::MoveCurrentPoint(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } bool isEditable = true; GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable); auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } auto planarFigureGeometry = planarFigure->GetPlaneGeometry(); if (nullptr == planarFigureGeometry) { return; } auto abstractTransformGeometry = dynamic_cast(planarFigure->GetGeometry(0)); if (nullptr != abstractTransformGeometry) { return; } // Extract point in 2D world coordinates (relative to PlaneGeometry of PlanarFigure) Point2D point2D; if (!this->TransformPositionEventToPoint2D(positionEvent, planarFigureGeometry, point2D) || !isEditable) { return; } planarFigure->InvokeEvent(StartInteractionPlanarFigureEvent()); // check if the control points shall be hidden during interaction bool hidecontrolpointsduringinteraction = false; GetDataNode()->GetBoolProperty("planarfigure.hidecontrolpointsduringinteraction", hidecontrolpointsduringinteraction); // hide the control points if necessary // interactionEvent->GetSender()->GetDataStorage()->BlockNodeModifiedEvents( true ); GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", !hidecontrolpointsduringinteraction); // interactionEvent->GetSender()->GetDataStorage()->BlockNodeModifiedEvents( false ); // Move current control point to this point planarFigure->SetCurrentControlPoint(point2D); // Re-evaluate features planarFigure->EvaluateFeatures(); // Update rendered scene RenderingManager::GetInstance()->RequestUpdateAll(); planarFigure->InvokeEvent(PointMovedPlanarFigureEvent()); } void mitk::PlanarFigureInteractor::FinalizeFigure(StateMachineAction *, InteractionEvent *) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } planarFigure->Modified(); planarFigure->DeselectControlPoint(); planarFigure->RemoveLastControlPoint(); planarFigure->SetProperty("initiallyplaced", mitk::BoolProperty::New(true)); GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true); GetDataNode()->Modified(); planarFigure->InvokeEvent(EndPlacementPlanarFigureEvent()); planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent()); // Shape might change when figure is finalized, e.g., smoothing of subdivision polygon planarFigure->EvaluateFeatures(); RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PlanarFigureInteractor::EndInteraction(StateMachineAction *, InteractionEvent *) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true); planarFigure->Modified(); planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent()); RenderingManager::GetInstance()->RequestUpdateAll(); } bool mitk::PlanarFigureInteractor::FilterEvents(InteractionEvent *interactionEvent, mitk::DataNode * /*dataNode*/) { if (interactionEvent->GetSender() == nullptr) return false; if (interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard3D) return false; return true; } void mitk::PlanarFigureInteractor::EndHovering(StateMachineAction *, InteractionEvent *) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } planarFigure->ResetPreviewContolPoint(); // Invoke end-hover event once the mouse is exiting the figure area m_IsHovering = false; planarFigure->InvokeEvent(EndHoverPlanarFigureEvent()); // Set bool property to indicate that planar figure is no longer in "hovering" mode GetDataNode()->SetBoolProperty("planarfigure.ishovering", false); RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PlanarFigureInteractor::DeleteFigure(StateMachineAction *, InteractionEvent *interactionEvent) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } planarFigure->RemoveAllObservers(); GetDataNode()->RemoveAllObservers(); interactionEvent->GetSender()->GetDataStorage()->Remove(GetDataNode()); RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PlanarFigureInteractor::PerformPointResetOnSelect(StateMachineAction *, InteractionEvent *) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } planarFigure->ResetOnPointSelect(); } bool mitk::PlanarFigureInteractor::CheckMinimalFigureFinished(const InteractionEvent * /*interactionEvent*/) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return false; } return planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMinimumNumberOfControlPoints(); } bool mitk::PlanarFigureInteractor::CheckFigureFinished(const InteractionEvent * /*interactionEvent*/) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return false; } return planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMaximumNumberOfControlPoints(); } bool mitk::PlanarFigureInteractor::CheckFigureIsExtendable(const InteractionEvent * /*interactionEvent*/) { bool isExtendable(false); GetDataNode()->GetBoolProperty("planarfigure.isextendable", isExtendable); return isExtendable; } bool mitk::PlanarFigureInteractor::CheckFigureIsDeletable(const InteractionEvent * /*interactionEvent*/) { bool isDeletable(true); GetDataNode()->GetBoolProperty("planarfigure.isdeletable", isDeletable); return isDeletable; } bool mitk::PlanarFigureInteractor::CheckFigureIsEditable(const InteractionEvent * /*interactionEvent*/) { bool isEditable(true); GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable); return isEditable; } void mitk::PlanarFigureInteractor::DeselectPoint(StateMachineAction *, InteractionEvent * /*interactionEvent*/) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } const bool wasSelected = planarFigure->DeselectControlPoint(); if (wasSelected) { // Issue event so that listeners may update themselves planarFigure->Modified(); planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent()); GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true); GetDataNode()->Modified(); } } void mitk::PlanarFigureInteractor::AddPoint(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } /* * Added check for "initiallyplaced" due to bug 13097: * * There are two possible cases in which a point can be inserted into a PlanarPolygon: * * 1. The figure is currently drawn -> the point will be appended at the end of the figure * 2. A point is inserted at a userdefined position after the initial placement of the figure is finished * * In the second case we need to determine the proper insertion index. In the first case the index always has * to be -1 so that the point is appended to the end. * * These changes are necessary because of a macOS specific issue: If a users draws a PlanarPolygon then the * next point to be added moves according to the mouse position. If then the user left clicks in order to add * a point one would assume the last move position is identical to the left click position. This is actually the * case for windows and linux but somehow NOT for mac. Because of the insertion logic of a new point in the * PlanarFigure then for mac the wrong current selected point is determined. * * With this check here this problem can be avoided. However a redesign of the insertion logic should be considered */ const DataNode::Pointer node = this->GetDataNode(); const BaseData::Pointer data = node->GetData(); bool isFigureFinished = false; data->GetPropertyList()->GetBoolProperty("initiallyplaced", isFigureFinished); bool selected = false; bool isEditable = true; node->GetBoolProperty("selected", selected); node->GetBoolProperty("planarfigure.iseditable", isEditable); if (!selected || !isEditable) { return; } auto planarFigure = dynamic_cast(data.GetPointer()); if (nullptr == planarFigure) { return; } // We can't derive a new control point from a polyline of a Bezier curve // as all control points contribute to each polyline point. if (dynamic_cast(planarFigure) != nullptr && isFigureFinished) return; auto planarFigureGeometry = planarFigure->GetPlaneGeometry(); if (nullptr == planarFigureGeometry) { return; } auto abstractTransformGeometry = dynamic_cast(planarFigure->GetGeometry(0)); if (nullptr != abstractTransformGeometry) { return; } // If the planarFigure already has reached the maximum number if (planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMaximumNumberOfControlPoints()) { return; } // Extract point in 2D world coordinates (relative to PlaneGeometry of // PlanarFigure) Point2D point2D, projectedPoint; if (!this->TransformPositionEventToPoint2D(positionEvent, planarFigureGeometry, point2D)) { return; } // TODO: check segment of polyline we clicked in int nextIndex = -1; // We only need to check which position to insert the control point // when interacting with a PlanarPolygon. For all other types // new control points will always be appended const mitk::BaseRenderer *renderer = interactionEvent->GetSender(); const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry(); if (dynamic_cast(planarFigure) && isFigureFinished) { nextIndex = this->IsPositionOverFigure(positionEvent, planarFigure, planarFigureGeometry, projectionPlane, projectedPoint); } // Add point as new control point if (planarFigure->IsPreviewControlPointVisible()) { point2D = planarFigure->GetPreviewControlPoint(); } planarFigure->AddControlPoint(point2D, planarFigure->GetControlPointForPolylinePoint(nextIndex, 0)); if (planarFigure->IsPreviewControlPointVisible()) { planarFigure->SelectControlPoint(nextIndex); planarFigure->ResetPreviewContolPoint(); } // Re-evaluate features planarFigure->EvaluateFeatures(); // this->LogPrintPlanarFigureQuantities( planarFigure ); // Update rendered scene RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PlanarFigureInteractor::AddInitialPoint(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } mitk::BaseRenderer *renderer = interactionEvent->GetSender(); auto abstractTransformGeometry = dynamic_cast(planarFigure->GetGeometry(0)); // Invoke event to notify listeners that placement of this PF starts now planarFigure->InvokeEvent(StartPlacementPlanarFigureEvent()); // Use PlaneGeometry of the renderer clicked on for this PlanarFigure auto *planeGeometry = const_cast( dynamic_cast(renderer->GetSliceNavigationController()->GetCurrentPlaneGeometry())); if (planeGeometry != nullptr && abstractTransformGeometry == nullptr) { planarFigure->SetPlaneGeometry(planeGeometry); } else { return; } // Extract point in 2D world coordinates (relative to PlaneGeometry of // PlanarFigure) Point2D point2D; if (!this->TransformPositionEventToPoint2D(positionEvent, planeGeometry, point2D)) { return; } // Place PlanarFigure at this point planarFigure->PlaceFigure(point2D); // Re-evaluate features planarFigure->EvaluateFeatures(); // this->LogPrintPlanarFigureQuantities( planarFigure ); // Set a bool property indicating that the figure has been placed in // the current RenderWindow. This is required so that the same render // window can be re-aligned to the PlaneGeometry of the PlanarFigure later // on in an application. GetDataNode()->SetBoolProperty("PlanarFigureInitializedWindow", true, renderer); // Update rendered scene RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PlanarFigureInteractor::StartHovering(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } if (!m_IsHovering) { // Invoke hover event once when the mouse is entering the figure area m_IsHovering = true; planarFigure->InvokeEvent(StartHoverPlanarFigureEvent()); // Set bool property to indicate that planar figure is currently in "hovering" mode GetDataNode()->SetBoolProperty("planarfigure.ishovering", true); RenderingManager::GetInstance()->RequestUpdateAll(); } } void mitk::PlanarFigureInteractor::SetPreviewPointPosition(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } const mitk::BaseRenderer *renderer = interactionEvent->GetSender(); planarFigure->DeselectControlPoint(); mitk::Point2D pointProjectedOntoLine = positionEvent->GetPointerPositionOnScreen(); bool selected(false); bool isExtendable(false); bool isEditable(true); GetDataNode()->GetBoolProperty("selected", selected); GetDataNode()->GetBoolProperty("planarfigure.isextendable", isExtendable); GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable); if (selected && isExtendable && isEditable) { renderer->DisplayToPlane(pointProjectedOntoLine, pointProjectedOntoLine); planarFigure->SetPreviewControlPoint(pointProjectedOntoLine); } RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PlanarFigureInteractor::HideControlPoints(StateMachineAction *, InteractionEvent * /*interactionEvent*/) { GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", false); } void mitk::PlanarFigureInteractor::HidePreviewPoint(StateMachineAction *, InteractionEvent *) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } planarFigure->ResetPreviewContolPoint(); RenderingManager::GetInstance()->RequestUpdateAll(); } bool mitk::PlanarFigureInteractor::CheckFigureHovering(const InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return false; } auto planarFigureGeometry = planarFigure->GetPlaneGeometry(); if (nullptr == planarFigureGeometry) { return false; } auto abstractTransformGeometry = dynamic_cast(planarFigure->GetGeometry(0)); if (nullptr != abstractTransformGeometry) { return false; } const mitk::BaseRenderer *renderer = interactionEvent->GetSender(); const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry(); mitk::Point2D pointProjectedOntoLine; int previousControlPoint = this->IsPositionOverFigure( positionEvent, planarFigure, planarFigureGeometry, projectionPlane, pointProjectedOntoLine); bool isHovering = (previousControlPoint != -1); return isHovering; } bool mitk::PlanarFigureInteractor::CheckControlPointHovering(const InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return false; } auto planarFigureGeometry = planarFigure->GetPlaneGeometry(); if (nullptr == planarFigureGeometry) { return false; } auto abstractTransformGeometry = dynamic_cast(planarFigure->GetGeometry(0)); if (nullptr != abstractTransformGeometry) { return false; } const mitk::BaseRenderer *renderer = interactionEvent->GetSender(); const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry(); int pointIndex = -1; pointIndex = mitk::PlanarFigureInteractor::IsPositionInsideMarker( positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer); return pointIndex >= 0; } bool mitk::PlanarFigureInteractor::CheckSelection(const InteractionEvent * /*interactionEvent*/) { bool selected = false; GetDataNode()->GetBoolProperty("selected", selected); return selected; } void mitk::PlanarFigureInteractor::SelectFigure(StateMachineAction *, InteractionEvent * /*interactionEvent*/) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } planarFigure->InvokeEvent(SelectPlanarFigureEvent()); } void mitk::PlanarFigureInteractor::SelectPoint(StateMachineAction *, InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return; } auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } auto planarFigureGeometry = planarFigure->GetPlaneGeometry(); if (nullptr == planarFigureGeometry) { return; } auto abstractTransformGeometry = dynamic_cast(planarFigure->GetGeometry(0)); if (nullptr != abstractTransformGeometry) { return; } const mitk::BaseRenderer *renderer = interactionEvent->GetSender(); const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry(); const int pointIndex = mitk::PlanarFigureInteractor::IsPositionInsideMarker( positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer); if (pointIndex >= 0) { // If mouse is above control point, mark it as selected planarFigure->SelectControlPoint(pointIndex); } else { planarFigure->DeselectControlPoint(); } } bool mitk::PlanarFigureInteractor::CheckPointValidity(const InteractionEvent *interactionEvent) { // Check if the distance of the current point to the previously set point in display coordinates // is sufficient (if a previous point exists) auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return false; } return IsMousePositionAcceptableAsNewControlPoint(positionEvent, planarFigure); } void mitk::PlanarFigureInteractor::RemoveSelectedPoint(StateMachineAction *, InteractionEvent *interactionEvent) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } const int selectedControlPoint = planarFigure->GetSelectedControlPoint(); planarFigure->RemoveControlPoint(selectedControlPoint); // Re-evaluate features planarFigure->EvaluateFeatures(); planarFigure->Modified(); GetDataNode()->SetBoolProperty("planarfigure.drawcontrolpoints", true); planarFigure->InvokeEvent(EndInteractionPlanarFigureEvent()); RenderingManager::GetInstance()->RequestUpdateAll(); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); HandleEvent(mitk::InternalEvent::New(renderer, this, "Dummy-Event"), GetDataNode()); } void mitk::PlanarFigureInteractor::RequestContextMenu(StateMachineAction *, InteractionEvent * /*interactionEvent*/) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return; } bool selected = false; GetDataNode()->GetBoolProperty("selected", selected); // no need to invoke this if the figure is already selected if (!selected) { planarFigure->InvokeEvent(SelectPlanarFigureEvent()); } planarFigure->InvokeEvent(ContextMenuPlanarFigureEvent()); } bool mitk::PlanarFigureInteractor::CheckResetOnPointSelect(const InteractionEvent * /*interactionEvent*/) { auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return false; } bool isEditable = true; GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable); // Reset the PlanarFigure if required return isEditable && planarFigure->ResetOnPointSelectNeeded(); } bool mitk::PlanarFigureInteractor::CheckFigureOnRenderingGeometry(const InteractionEvent *interactionEvent) { auto positionEvent = dynamic_cast(interactionEvent); if (nullptr == positionEvent) { return false; } const mitk::Point3D worldPoint3D = positionEvent->GetPositionInWorld(); auto planarFigure = dynamic_cast(GetDataNode()->GetData()); if (nullptr == planarFigure) { return false; } auto planarFigureGeometry = planarFigure->GetPlaneGeometry(); if (nullptr == planarFigureGeometry) { return false; } auto abstractTransformGeometry = dynamic_cast(planarFigure->GetGeometry(0)); if (nullptr != abstractTransformGeometry) { return false; } const double planeThickness = planarFigureGeometry->GetExtentInMM(2); return planarFigureGeometry->Distance(worldPoint3D) <= planeThickness; } void mitk::PlanarFigureInteractor::SetPrecision(mitk::ScalarType precision) { m_Precision = precision; } void mitk::PlanarFigureInteractor::SetMinimumPointDistance(ScalarType minimumDistance) { m_MinimumPointDistance = minimumDistance; } bool mitk::PlanarFigureInteractor::TransformPositionEventToPoint2D(const InteractionPositionEvent *positionEvent, const PlaneGeometry *planarFigureGeometry, Point2D &point2D) { if (nullptr == positionEvent || nullptr == planarFigureGeometry) { return false; } const mitk::Point3D worldPoint3D = positionEvent->GetPositionInWorld(); // TODO: proper handling of distance tolerance if (planarFigureGeometry->Distance(worldPoint3D) > 0.1) { return false; } // Project point onto plane of this PlanarFigure planarFigureGeometry->Map(worldPoint3D, point2D); return true; } bool mitk::PlanarFigureInteractor::TransformObjectToDisplay(const mitk::Point2D &point2D, mitk::Point2D &displayPoint, const mitk::PlaneGeometry *objectGeometry, const mitk::PlaneGeometry *rendererGeometry, const mitk::BaseRenderer *renderer) const { if (nullptr == objectGeometry || nullptr == rendererGeometry || nullptr == renderer) { return false; } mitk::Point3D point3D; // Map circle point from local 2D geometry into 3D world space objectGeometry->Map(point2D, point3D); const double planeThickness = objectGeometry->GetExtentInMM(2); // TODO: proper handling of distance tolerance if (rendererGeometry->Distance(point3D) < planeThickness / 3.0) { // Project 3D world point onto display geometry renderer->WorldToDisplay(point3D, displayPoint); return true; } return false; } bool mitk::PlanarFigureInteractor::IsPointNearLine(const mitk::Point2D &point, const mitk::Point2D &startPoint, const mitk::Point2D &endPoint, mitk::Point2D &projectedPoint) const { mitk::Vector2D n1 = endPoint - startPoint; n1.Normalize(); // Determine dot products between line vector and startpoint-point / endpoint-point vectors const double l1 = n1 * (point - startPoint); const double l2 = -n1 * (point - endPoint); // Determine projection of specified point onto line defined by start / end point const mitk::Point2D crossPoint = startPoint + n1 * l1; projectedPoint = crossPoint; const float dist1 = crossPoint.SquaredEuclideanDistanceTo(point); const float dist2 = endPoint.SquaredEuclideanDistanceTo(point); const float dist3 = startPoint.SquaredEuclideanDistanceTo(point); // Point is inside encompassing rectangle IF // - its distance to its projected point is small enough // - it is not further outside of the line than the defined tolerance if (((dist1 < 20.0) && (l1 > 0.0) && (l2 > 0.0)) || dist2 < 20.0 || dist3 < 20.0) { return true; } return false; } int mitk::PlanarFigureInteractor::IsPositionOverFigure(const InteractionPositionEvent *positionEvent, PlanarFigure *planarFigure, const PlaneGeometry *planarFigureGeometry, const PlaneGeometry *rendererGeometry, Point2D &pointProjectedOntoLine) const { if (nullptr == positionEvent || nullptr == planarFigure || nullptr == planarFigureGeometry || nullptr == rendererGeometry) { return -1; } mitk::Point2D displayPosition = positionEvent->GetPointerPositionOnScreen(); // Iterate over all polylines of planar figure, and check if // any one is close to the current display position typedef mitk::PlanarFigure::PolyLineType VertexContainerType; Point2D polyLinePoint; Point2D firstPolyLinePoint; Point2D previousPolyLinePoint; for (unsigned short loop = 0; loop < planarFigure->GetPolyLinesSize(); ++loop) { const VertexContainerType polyLine = planarFigure->GetPolyLine(loop); bool firstPoint(true); for (auto it = polyLine.begin(); it != polyLine.end(); ++it) { // Get plane coordinates of this point of polyline (if possible) if (!this->TransformObjectToDisplay( *it, polyLinePoint, planarFigureGeometry, rendererGeometry, positionEvent->GetSender())) { break; // Poly line invalid (not on current 2D plane) --> skip it } if (firstPoint) { firstPolyLinePoint = polyLinePoint; firstPoint = false; } else if (this->IsPointNearLine(displayPosition, previousPolyLinePoint, polyLinePoint, pointProjectedOntoLine)) { // Point is close enough to line segment --> Return index of the segment return std::distance(polyLine.begin(), it); } previousPolyLinePoint = polyLinePoint; } // For closed figures, also check last line segment if (planarFigure->IsClosed() && this->IsPointNearLine(displayPosition, polyLinePoint, firstPolyLinePoint, pointProjectedOntoLine)) { return 0; // Return index of first control point } } return -1; } int mitk::PlanarFigureInteractor::IsPositionInsideMarker(const InteractionPositionEvent *positionEvent, const PlanarFigure *planarFigure, const PlaneGeometry *planarFigureGeometry, const PlaneGeometry *rendererGeometry, const BaseRenderer *renderer) const { if (nullptr == positionEvent || nullptr == planarFigure || nullptr == planarFigureGeometry || nullptr == rendererGeometry || nullptr == renderer) { return -1; } const mitk::Point2D displayPosition = positionEvent->GetPointerPositionOnScreen(); // Iterate over all control points of planar figure, and check if // any one is close to the current display position mitk::Point2D displayControlPoint; const int numberOfControlPoints = planarFigure->GetNumberOfControlPoints(); for (int i = 0; i < numberOfControlPoints; i++) { if (this->TransformObjectToDisplay( planarFigure->GetControlPoint(i), displayControlPoint, planarFigureGeometry, rendererGeometry, renderer)) { // TODO: variable size of markers if (displayPosition.SquaredEuclideanDistanceTo(displayControlPoint) < 20.0) { return i; } } } return -1; } void mitk::PlanarFigureInteractor::LogPrintPlanarFigureQuantities(const PlanarFigure *planarFigure) { if (nullptr == planarFigure) { MITK_INFO << "PlanarFigure invalid."; } MITK_INFO << "PlanarFigure: " << planarFigure->GetNameOfClass(); for (unsigned int i = 0; i < planarFigure->GetNumberOfFeatures(); ++i) { MITK_INFO << "* " << planarFigure->GetFeatureName(i) << ": " << planarFigure->GetQuantity(i) << " " << planarFigure->GetFeatureUnit(i); } } bool mitk::PlanarFigureInteractor::IsMousePositionAcceptableAsNewControlPoint( const mitk::InteractionPositionEvent *positionEvent, const PlanarFigure *planarFigure) { if (nullptr == positionEvent || nullptr == planarFigure) { return false; } const BaseRenderer *renderer = positionEvent->GetSender(); if (nullptr == renderer) { return false; } // Get the timestep to support 3D+t const int timeStep(renderer->GetTimeStep(planarFigure)); bool tooClose(false); auto planarFigureGeometry = dynamic_cast(planarFigure->GetGeometry(timeStep)); if (nullptr == planarFigureGeometry) { return false; } auto abstractTransformGeometry = dynamic_cast(planarFigure->GetGeometry(timeStep)); if (nullptr != abstractTransformGeometry) { return false; } Point2D point2D; // Get the point2D from the positionEvent if (!this->TransformPositionEventToPoint2D(positionEvent, planarFigureGeometry, point2D)) { return false; } // apply the controlPoint constraints of the planarFigure to get the // coordinates that would actually be used. const Point2D correctedPoint = const_cast(planarFigure)->ApplyControlPointConstraints(0, point2D); // map the 2D coordinates of the new point to world-coordinates // and transform those to display-coordinates mitk::Point3D newPoint3D; planarFigureGeometry->Map(correctedPoint, newPoint3D); mitk::Point2D newDisplayPosition; renderer->WorldToDisplay(newPoint3D, newDisplayPosition); const int selectedControlPoint = planarFigure->GetSelectedControlPoint(); for (int i = 0; i < (int)planarFigure->GetNumberOfControlPoints(); ++i) { if (i != selectedControlPoint) { // Try to convert previous point to current display coordinates mitk::Point3D previousPoint3D; // map the 2D coordinates of the control-point to world-coordinates planarFigureGeometry->Map(planarFigure->GetControlPoint(i), previousPoint3D); if (renderer->GetCurrentWorldPlaneGeometry()->Distance(previousPoint3D) < 0.1) // ugly, but assert makes this work { mitk::Point2D previousDisplayPosition; // transform the world-coordinates into display-coordinates renderer->WorldToDisplay(previousPoint3D, previousDisplayPosition); // Calculate the distance. We use display-coordinates here to make // the check independent of the zoom-level of the rendering scene. const double a = newDisplayPosition[0] - previousDisplayPosition[0]; const double b = newDisplayPosition[1] - previousDisplayPosition[1]; // If point is to close, do not set a new point tooClose = (a * a + b * b < m_MinimumPointDistance); } if (tooClose) return false; // abort loop early } } return !tooClose; // default } void mitk::PlanarFigureInteractor::ConfigurationChanged() { const mitk::PropertyList::Pointer properties = GetAttributes(); std::string precision = ""; if (properties->GetStringProperty("precision", precision)) { m_Precision = atof(precision.c_str()); } else { m_Precision = (ScalarType)6.5; } std::string minPointDistance = ""; if (properties->GetStringProperty("minPointDistance", minPointDistance)) { m_MinimumPointDistance = atof(minPointDistance.c_str()); } else { m_MinimumPointDistance = (ScalarType)25.0; } } diff --git a/Modules/Python/autoload/PythonService/mitkPythonService.cpp b/Modules/Python/autoload/PythonService/mitkPythonService.cpp index 4dd7ae1c18..513a7fe647 100644 --- a/Modules/Python/autoload/PythonService/mitkPythonService.cpp +++ b/Modules/Python/autoload/PythonService/mitkPythonService.cpp @@ -1,917 +1,917 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPythonService.h" #include #include #include #include #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 5208) #endif #include #ifdef _MSC_VER # pragma warning(pop) #endif #include "PythonPath.h" #include #include #include #include #include #include #include #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include #ifndef WIN32 #include #endif typedef itksys::SystemTools ist; mitk::PythonService::PythonService() : m_ItkWrappingAvailable( true ) , m_OpenCVWrappingAvailable( true ) , m_VtkWrappingAvailable( true ) , m_ErrorOccured( false ) { bool pythonInitialized = static_cast( Py_IsInitialized() ); //m_PythonManager.isPythonInitialized() ); // due to strange static var behaviour on windows Py_IsInitialized() returns correct value while // m_PythonManager.isPythonInitialized() does not because it has been constructed and destructed again if( !pythonInitialized ) { MITK_INFO << "Initializing python service"; //TODO a better way to do this #ifndef WIN32 dlerror(); if(dlopen(PYTHON_LIBRARY, RTLD_NOW | RTLD_GLOBAL) == nullptr ) { mitkThrow() << "Python runtime could not be loaded: " << dlerror(); } #endif std::string programPath = QCoreApplication::applicationDirPath().toStdString() + "/"; QString pythonCommand; pythonCommand.append( QString("import site, sys\n") ); pythonCommand.append( QString("import SimpleITK as sitk\n") ); pythonCommand.append( QString("import SimpleITK._SimpleITK as _SimpleITK\n") ); pythonCommand.append( QString("import numpy\n") ); pythonCommand.append( QString("sys.path.append('')\n") ); pythonCommand.append( QString("sys.path.append('%1')\n").arg(programPath.c_str()) ); pythonCommand.append( QString("sys.path.append('%1')\n").arg(EXTERNAL_DIST_PACKAGES) ); pythonCommand.append( QString("\nsite.addsitedir('%1')").arg(EXTERNAL_SITE_PACKAGES) ); if( pythonInitialized ) m_PythonManager.setInitializationFlags(PythonQt::RedirectStdOut|PythonQt::PythonAlreadyInitialized); else m_PythonManager.setInitializationFlags(PythonQt::RedirectStdOut); m_PythonManager.initialize(); m_PythonManager.executeString( pythonCommand, ctkAbstractPythonManager::FileInput ); } } mitk::PythonService::~PythonService() { MITK_DEBUG("mitk::PythonService") << "destructing PythonService"; } void mitk::PythonService::AddRelativeSearchDirs(std::vector< std::string > dirs) { std::string programPath = QCoreApplication::applicationDirPath().toStdString() + "/"; std::string cwd = ist::GetCurrentWorkingDirectory() + "/"; for (auto dir : dirs) { m_PythonManager.executeString(QString("sys.path.append('%1')").arg((programPath + dir).c_str()), ctkAbstractPythonManager::SingleInput ); m_PythonManager.executeString(QString("sys.path.append('%1')").arg((cwd + dir).c_str()), ctkAbstractPythonManager::SingleInput ); } } void mitk::PythonService::AddAbsoluteSearchDirs(std::vector< std::string > dirs) { for (auto dir : dirs) { m_PythonManager.executeString(QString("sys.path.append('%1')").arg(dir.c_str()), ctkAbstractPythonManager::SingleInput ); } } std::string mitk::PythonService::Execute(const std::string &stdpythonCommand, int commandType) { QString pythonCommand = QString::fromStdString(stdpythonCommand); QVariant result; bool commandIssued = true; if(commandType == IPythonService::SINGLE_LINE_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::SingleInput ); else if(commandType == IPythonService::MULTI_LINE_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::FileInput ); else if(commandType == IPythonService::EVAL_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::EvalInput ); else commandIssued = false; if(commandIssued) { this->NotifyObserver(pythonCommand.toStdString()); m_ErrorOccured = PythonQt::self()->hadError(); } return result.toString().toStdString(); } void mitk::PythonService::ExecuteScript( const std::string& pythonScript ) { std::ifstream t(pythonScript.c_str()); std::string str((std::istreambuf_iterator(t)), std::istreambuf_iterator()); t.close(); m_PythonManager.executeString(QString::fromStdString(str)); } std::vector mitk::PythonService::GetVariableStack() const { std::vector list; PyObject* dict = PyImport_GetModuleDict(); PyObject* object = PyDict_GetItemString(dict, "__main__"); PyObject* dirMain = PyObject_Dir(object); PyObject* tempObject = nullptr; //PyObject* strTempObject = 0; if(dirMain) { std::string name, attrValue, attrType; for(int i = 0; iob_type->tp_name; if(tempObject && ( PyUnicode_Check(tempObject) || PyString_Check(tempObject) ) ) attrValue = PyString_AsString(tempObject); else attrValue = ""; mitk::PythonVariable var; var.m_Name = name; var.m_Value = attrValue; var.m_Type = attrType; list.push_back(var); } } return list; } std::string mitk::PythonService::GetVariable(const std::string& name) const { std::vector allVars = this->GetVariableStack(); for(unsigned int i = 0; i< allVars.size(); i++) { if( allVars.at(i).m_Name == name ) return allVars.at(i).m_Value; } return ""; } bool mitk::PythonService::DoesVariableExist(const std::string& name) const { bool varExists = false; std::vector allVars = this->GetVariableStack(); for(unsigned int i = 0; i< allVars.size(); i++) { if( allVars.at(i).m_Name == name ) { varExists = true; break; } } return varExists; } void mitk::PythonService::AddPythonCommandObserver(mitk::PythonCommandObserver *observer) { if(!m_Observer.contains(observer)) m_Observer.append(observer); } void mitk::PythonService::RemovePythonCommandObserver(mitk::PythonCommandObserver *observer) { m_Observer.removeOne(observer); } void mitk::PythonService::NotifyObserver(const std::string &command) { MITK_DEBUG("mitk::PythonService") << "number of observer " << m_Observer.size(); for( int i=0; i< m_Observer.size(); ++i ) { m_Observer.at(i)->CommandExecuted(command); } } bool mitk::PythonService::CopyToPythonAsSimpleItkImage(mitk::Image *image, const std::string &stdvarName) { QString varName = QString::fromStdString( stdvarName ); QString command; unsigned int* imgDim = image->GetDimensions(); int npy_nd = 1; // access python module PyObject *pyMod = PyImport_AddModule("__main__"); // global dictionary PyObject *pyDict = PyModule_GetDict(pyMod); const mitk::Vector3D spacing = image->GetGeometry()->GetSpacing(); const mitk::Point3D origin = image->GetGeometry()->GetOrigin(); mitk::PixelType pixelType = image->GetPixelType(); - itk::ImageIOBase::IOPixelType ioPixelType = image->GetPixelType().GetPixelType(); + auto ioPixelType = image->GetPixelType().GetPixelType(); PyObject* npyArray = nullptr; mitk::ImageReadAccessor racc(image); void* array = const_cast(racc.GetData()); mitk::Vector3D xDirection; mitk::Vector3D yDirection; mitk::Vector3D zDirection; const vnl_matrix_fixed &transform = image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::Vector3D s = image->GetGeometry()->GetSpacing(); // ToDo: Check if this is a collumn or row vector from the matrix. // right now it works but not sure for rotated geometries mitk::FillVector3D(xDirection, transform[0][0]/s[0], transform[0][1]/s[1], transform[0][2]/s[2]); mitk::FillVector3D(yDirection, transform[1][0]/s[0], transform[1][1]/s[1], transform[1][2]/s[2]); mitk::FillVector3D(zDirection, transform[2][0]/s[0], transform[2][1]/s[1], transform[2][2]/s[2]); // save the total number of elements here (since the numpy array is one dimensional) npy_intp* npy_dims = new npy_intp[1]; npy_dims[0] = imgDim[0]; /** * Build a string in the format [1024,1028,1] * to describe the dimensionality. This is needed for simple itk * to know the dimensions of the image */ QString dimensionString; dimensionString.append(QString("[")); dimensionString.append(QString::number(imgDim[0])); for (unsigned i = 1; i < 3; ++i) // always three because otherwise the 3d-geometry gets destroyed // (relevant for backtransformation of simple itk image to mitk. { dimensionString.append(QString(",")); dimensionString.append(QString::number(imgDim[i])); npy_dims[0] *= imgDim[i]; } dimensionString.append("]"); // the next line is necessary for vectorimages npy_dims[0] *= pixelType.GetNumberOfComponents(); // default pixeltype: unsigned short NPY_TYPES npy_type = NPY_USHORT; std::string sitk_type = "sitkUInt8"; - if( ioPixelType == itk::ImageIOBase::SCALAR ) + if( ioPixelType == itk::IOPixelEnum::SCALAR ) { - if( pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE ) { + if( pixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE ) { npy_type = NPY_DOUBLE; sitk_type = "sitkFloat64"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::FLOAT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::FLOAT ) { npy_type = NPY_FLOAT; sitk_type = "sitkFloat32"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::SHORT) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::SHORT) { npy_type = NPY_SHORT; sitk_type = "sitkInt16"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::CHAR ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::CHAR ) { npy_type = NPY_BYTE; sitk_type = "sitkInt8"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::INT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::INT ) { npy_type = NPY_INT; sitk_type = "sitkInt32"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::LONG ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::LONG ) { npy_type = NPY_LONG; sitk_type = "sitkInt64"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::UCHAR ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR ) { npy_type = NPY_UBYTE; sitk_type = "sitkUInt8"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::UINT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UINT ) { npy_type = NPY_UINT; sitk_type = "sitkUInt32"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::ULONG ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::ULONG ) { npy_type = NPY_LONG; sitk_type = "sitkUInt64"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::USHORT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::USHORT ) { npy_type = NPY_USHORT; sitk_type = "sitkUInt16"; } } - else if ( ioPixelType == itk::ImageIOBase::VECTOR || - ioPixelType == itk::ImageIOBase::RGB || - ioPixelType == itk::ImageIOBase::RGBA + else if ( ioPixelType == itk::IOPixelEnum::VECTOR || + ioPixelType == itk::IOPixelEnum::RGB || + ioPixelType == itk::IOPixelEnum::RGBA ) { - if( pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE ) { + if( pixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE ) { npy_type = NPY_DOUBLE; sitk_type = "sitkVectorFloat64"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::FLOAT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::FLOAT ) { npy_type = NPY_FLOAT; sitk_type = "sitkVectorFloat32"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::SHORT) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::SHORT) { npy_type = NPY_SHORT; sitk_type = "sitkVectorInt16"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::CHAR ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::CHAR ) { npy_type = NPY_BYTE; sitk_type = "sitkVectorInt8"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::INT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::INT ) { npy_type = NPY_INT; sitk_type = "sitkVectorInt32"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::LONG ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::LONG ) { npy_type = NPY_LONG; sitk_type = "sitkVectorInt64"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::UCHAR ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR ) { npy_type = NPY_UBYTE; sitk_type = "sitkVectorUInt8"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::UINT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UINT ) { npy_type = NPY_UINT; sitk_type = "sitkVectorUInt32"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::ULONG ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::ULONG ) { npy_type = NPY_LONG; sitk_type = "sitkVectorUInt64"; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::USHORT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::USHORT ) { npy_type = NPY_USHORT; sitk_type = "sitkVectorUInt16"; } } else { MITK_WARN << "not a recognized pixeltype"; return false; } // creating numpy array import_array1 (true); npyArray = PyArray_SimpleNewFromData(npy_nd,npy_dims,npy_type,array); // add temp array it to the python dictionary to access it in python code const int status = PyDict_SetItemString( pyDict,QString("%1_numpy_array") .arg(varName).toStdString().c_str(), npyArray ); // sanity check if ( status != 0 ) return false; command.append( QString("%1 = sitk.Image(%2,sitk.%3,%4)\n").arg(varName) .arg(dimensionString) .arg(QString(sitk_type.c_str())).arg(QString::number(pixelType.GetNumberOfComponents())) ); command.append( QString("%1.SetSpacing([%2,%3,%4])\n").arg(varName) .arg(QString::number(spacing[0])) .arg(QString::number(spacing[1])) .arg(QString::number(spacing[2])) ); command.append( QString("%1.SetOrigin([%2,%3,%4])\n").arg(varName) .arg(QString::number(origin[0])) .arg(QString::number(origin[1])) .arg(QString::number(origin[2])) ); command.append( QString("%1.SetDirection([%2,%3,%4,%5,%6,%7,%8,%9,%10])\n").arg(varName) .arg(QString::number(xDirection[0])) .arg(QString::number(xDirection[1])) .arg(QString::number(xDirection[2])) .arg(QString::number(yDirection[0])) .arg(QString::number(yDirection[1])) .arg(QString::number(yDirection[2])) .arg(QString::number(zDirection[0])) .arg(QString::number(zDirection[1])) .arg(QString::number(zDirection[2])) ); // directly access the cpp api from the lib command.append( QString("_SimpleITK._SetImageFromArray(%1_numpy_array,%1)\n").arg(varName) ); command.append( QString("del %1_numpy_array").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute( command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return true; } mitk::PixelType DeterminePixelType(const std::string& pythonPixeltype, unsigned long nrComponents, int dimensions) { typedef itk::RGBPixel< unsigned char > UCRGBPixelType; typedef itk::RGBPixel< unsigned short > USRGBPixelType; typedef itk::RGBPixel< float > FloatRGBPixelType; typedef itk::RGBPixel< double > DoubleRGBPixelType; typedef itk::Image< UCRGBPixelType > UCRGBImageType; typedef itk::Image< USRGBPixelType > USRGBImageType; typedef itk::Image< FloatRGBPixelType > FloatRGBImageType; typedef itk::Image< DoubleRGBPixelType > DoubleRGBImageType; typedef itk::RGBAPixel< unsigned char > UCRGBAPixelType; typedef itk::RGBAPixel< unsigned short > USRGBAPixelType; typedef itk::RGBAPixel< float > FloatRGBAPixelType; typedef itk::RGBAPixel< double > DoubleRGBAPixelType; typedef itk::Image< UCRGBAPixelType > UCRGBAImageType; typedef itk::Image< USRGBAPixelType > USRGBAImageType; typedef itk::Image< FloatRGBAPixelType > FloatRGBAImageType; typedef itk::Image< DoubleRGBAPixelType > DoubleRGBAImageType; auto pixelType = mitk::MakePixelType(nrComponents); if (nrComponents == 1) { if( pythonPixeltype.compare("float64") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("float32") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("int16") == 0) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("int8") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("int32") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("int64") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("uint8") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("uint32") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("uint64") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("uint16") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else { mitkThrow()<< "unknown scalar PixelType"; } } else if(nrComponents == 3 && dimensions == 2) { if( pythonPixeltype.compare("float64") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("float32") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("uint8") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("uint16") == 0 ) { pixelType = mitk::MakePixelType(); } } else if( (nrComponents == 4) && dimensions == 2 ) { if( pythonPixeltype.compare("float64") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("float32") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("uint8") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("uint16") == 0 ) { pixelType = mitk::MakePixelType(); } } else { if( pythonPixeltype.compare("float64") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("float32") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("int16") == 0) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("int8") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("int32") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("int64") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("uint8") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("uint16") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("uint32") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("uint64") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else { mitkThrow()<< "unknown vectorial PixelType"; } } return pixelType; } mitk::Image::Pointer mitk::PythonService::CopySimpleItkImageFromPython(const std::string &stdvarName) { double*ds = nullptr; // access python module PyObject *pyMod = PyImport_AddModule("__main__"); // global dictionarry PyObject *pyDict = PyModule_GetDict(pyMod); mitk::Image::Pointer mitkImage = mitk::Image::New(); mitk::Vector3D spacing; mitk::Point3D origin; QString command; QString varName = QString::fromStdString( stdvarName ); command.append( QString("%1_numpy_array = sitk.GetArrayFromImage(%1)\n").arg(varName) ); command.append( QString("%1_spacing = numpy.asarray(%1.GetSpacing())\n").arg(varName) ); command.append( QString("%1_origin = numpy.asarray(%1.GetOrigin())\n").arg(varName) ); command.append( QString("%1_dtype = %1_numpy_array.dtype.name\n").arg(varName) ); command.append( QString("%1_direction = numpy.asarray(%1.GetDirection())\n").arg(varName) ); command.append( QString("%1_nrComponents = numpy.asarray(%1.GetNumberOfComponentsPerPixel())\n").arg(varName)); command.append( QString("%1_dtype = %1_numpy_array.dtype.name\n").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); PyObject* py_dtype = PyDict_GetItemString(pyDict,QString("%1_dtype").arg(varName).toStdString().c_str() ); std::string dtype = PyString_AsString(py_dtype); PyArrayObject* py_data = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_numpy_array").arg(varName).toStdString().c_str() ); PyArrayObject* py_spacing = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_spacing").arg(varName).toStdString().c_str() ); PyArrayObject* py_origin = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_origin").arg(varName).toStdString().c_str() ); PyArrayObject* py_direction = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_direction").arg(varName).toStdString().c_str() ); PyArrayObject* py_nrComponents = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_nrComponents").arg(varName).toStdString().c_str() ); unsigned int nr_Components = *(reinterpret_cast(PyArray_DATA(py_nrComponents))); unsigned int nr_dimensions = PyArray_NDIM(py_data); if (nr_Components > 1) // for VectorImages the last dimension in the numpy array are the vector components. { --nr_dimensions; } mitk::PixelType pixelType = DeterminePixelType(dtype, nr_Components, nr_dimensions); unsigned int* dimensions = new unsigned int[nr_dimensions]; // fill backwards , nd data saves dimensions in opposite direction for( unsigned i = 0; i < nr_dimensions; ++i ) { dimensions[i] = PyArray_DIMS(py_data)[nr_dimensions - 1 - i]; } mitkImage->Initialize(pixelType, nr_dimensions, dimensions); mitkImage->SetChannel(PyArray_DATA(py_data)); ds = reinterpret_cast(PyArray_DATA(py_spacing)); spacing[0] = ds[0]; spacing[1] = ds[1]; spacing[2] = ds[2]; mitkImage->GetGeometry()->SetSpacing(spacing); ds = reinterpret_cast(PyArray_DATA(py_origin)); origin[0] = ds[0]; origin[1] = ds[1]; origin[2] = ds[2]; mitkImage->GetGeometry()->SetOrigin(origin); itk::Matrix py_transform; ds = reinterpret_cast(PyArray_DATA(py_direction)); py_transform[0][0] = ds[0]; py_transform[0][1] = ds[1]; py_transform[0][2] = ds[2]; py_transform[1][0] = ds[3]; py_transform[1][1] = ds[4]; py_transform[1][2] = ds[5]; py_transform[2][0] = ds[6]; py_transform[2][1] = ds[7]; py_transform[2][2] = ds[8]; mitk::AffineTransform3D::Pointer affineTransform = mitkImage->GetGeometry()->GetIndexToWorldTransform(); itk::Matrix transform = py_transform * affineTransform->GetMatrix(); affineTransform->SetMatrix(transform); mitkImage->GetGeometry()->SetIndexToWorldTransform(affineTransform); // mitk::AffineTransform3D::New(); //mitkImage->GetGeometry()->SetIndexToWorldTransform(); // cleanup command.clear(); command.append( QString("del %1_numpy_array\n").arg(varName) ); command.append( QString("del %1_dtype\n").arg(varName) ); command.append( QString("del %1_spacing\n").arg(varName) ); command.append( QString("del %1_origin\n").arg(varName) ); command.append( QString("del %1_direction\n").arg(varName) ); command.append( QString("del %1_nrComponents\n").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); delete[] dimensions; return mitkImage; } bool mitk::PythonService::CopyToPythonAsCvImage( mitk::Image* image, const std::string& stdvarName ) { QString varName = QString::fromStdString( stdvarName ); QString command; unsigned int* imgDim = image->GetDimensions(); int npy_nd = 1; // access python module PyObject *pyMod = PyImport_AddModule((char*)"__main__"); // global dictionary PyObject *pyDict = PyModule_GetDict(pyMod); mitk::PixelType pixelType = image->GetPixelType(); PyObject* npyArray = nullptr; mitk::ImageReadAccessor racc(image); void* array = (void*) racc.GetData(); // save the total number of elements here (since the numpy array is one dimensional) npy_intp* npy_dims = new npy_intp[1]; npy_dims[0] = imgDim[0]; /** * Build a string in the format [1024,1028,1] * to describe the dimensionality. This is needed for simple itk * to know the dimensions of the image */ QString dimensionString; dimensionString.append(QString("[")); dimensionString.append(QString::number(imgDim[0])); // ToDo: check if we need this for (unsigned i = 1; i < 3; ++i) // always three because otherwise the 3d-geometry gets destroyed // (relevant for backtransformation of simple itk image to mitk. { dimensionString.append(QString(",")); dimensionString.append(QString::number(imgDim[i])); npy_dims[0] *= imgDim[i]; } dimensionString.append("]"); // the next line is necessary for vectorimages npy_dims[0] *= pixelType.GetNumberOfComponents(); // default pixeltype: unsigned short NPY_TYPES npy_type = NPY_USHORT; - if( pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE ) { + if( pixelType.GetComponentType() == itk::IOComponentEnum::DOUBLE ) { npy_type = NPY_DOUBLE; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::FLOAT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::FLOAT ) { npy_type = NPY_FLOAT; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::SHORT) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::SHORT) { npy_type = NPY_SHORT; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::CHAR ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::CHAR ) { npy_type = NPY_BYTE; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::INT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::INT ) { npy_type = NPY_INT; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::LONG ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::LONG ) { npy_type = NPY_LONG; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::UCHAR ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UCHAR ) { npy_type = NPY_UBYTE; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::UINT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::UINT ) { npy_type = NPY_UINT; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::ULONG ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::ULONG ) { npy_type = NPY_LONG; - } else if( pixelType.GetComponentType() == itk::ImageIOBase::USHORT ) { + } else if( pixelType.GetComponentType() == itk::IOComponentEnum::USHORT ) { npy_type = NPY_USHORT; } else { MITK_WARN << "not a recognized pixeltype"; return false; } // creating numpy array import_array1 (true); npyArray = PyArray_SimpleNewFromData(npy_nd,npy_dims,npy_type,array); // add temp array it to the python dictionary to access it in python code const int status = PyDict_SetItemString( pyDict,QString("%1_numpy_array") .arg(varName).toStdString().c_str(), npyArray ); // sanity check if ( status != 0 ) return false; command.append( QString("import numpy as np\n")); //command.append( QString("if '%1' in globals():\n").arg(varName)); //command.append( QString(" del %1\n").arg(varName)); command.append( QString("%1_array_tmp=%1_numpy_array.copy()\n").arg(varName)); command.append( QString("%1_array_tmp=%1_array_tmp.reshape(%2,%3,%4)\n").arg( varName, QString::number(imgDim[1]), QString::number(imgDim[0]), QString::number(pixelType.GetNumberOfComponents()))); command.append( QString("%1 = %1_array_tmp[:,...,::-1]\n").arg(varName)); command.append( QString("del %1_numpy_array\n").arg(varName) ); command.append( QString("del %1_array_tmp").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute( command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return true; } mitk::Image::Pointer mitk::PythonService::CopyCvImageFromPython( const std::string& stdvarName ) { // access python module PyObject *pyMod = PyImport_AddModule((char*)"__main__"); // global dictionarry PyObject *pyDict = PyModule_GetDict(pyMod); mitk::Image::Pointer mitkImage = mitk::Image::New(); QString command; QString varName = QString::fromStdString( stdvarName ); command.append( QString("import numpy as np\n")); command.append( QString("%1_dtype=%1.dtype.name\n").arg(varName) ); command.append( QString("%1_shape=np.asarray(%1.shape)\n").arg(varName) ); command.append( QString("%1_np_array=%1[:,...,::-1]\n").arg(varName)); command.append( QString("%1_np_array=np.reshape(%1_np_array,%1.shape[0] * %1.shape[1] * %1.shape[2])").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); PyObject* py_dtype = PyDict_GetItemString(pyDict,QString("%1_dtype").arg(varName).toStdString().c_str() ); std::string dtype = PyString_AsString(py_dtype); PyArrayObject* py_data = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_np_array").arg(varName).toStdString().c_str() ); PyArrayObject* shape = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_shape").arg(varName).toStdString().c_str() ); size_t* d = reinterpret_cast(PyArray_DATA(shape)); unsigned int dimensions[3]; dimensions[0] = d[1]; dimensions[1] = d[0]; dimensions[2] = d[2]; unsigned int nr_dimensions = 2; // get number of components unsigned int nr_Components = (unsigned int) d[2]; auto pixelType = DeterminePixelType(dtype, nr_Components, nr_dimensions); mitkImage->Initialize(pixelType, nr_dimensions, dimensions); //mitkImage->SetChannel(py_data->data); { mitk::ImageWriteAccessor ra(mitkImage); char* data = (char*)(ra.GetData()); memcpy(data, PyArray_DATA(py_data), dimensions[0] * dimensions[1] * pixelType.GetSize()); } command.clear(); command.append( QString("del %1_shape\n").arg(varName) ); command.append( QString("del %1_dtype\n").arg(varName) ); command.append( QString("del %1_np_array").arg(varName)); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return mitkImage; } ctkAbstractPythonManager *mitk::PythonService::GetPythonManager() { return &m_PythonManager; } mitk::Surface::Pointer mitk::PythonService::CopyVtkPolyDataFromPython( const std::string& stdvarName ) { // access python module PyObject *pyMod = PyImport_AddModule((char*)"__main__"); // global dictionarry PyObject *pyDict = PyModule_GetDict(pyMod); // python memory address PyObject *pyAddr = nullptr; // cpp address size_t addr = 0; mitk::Surface::Pointer surface = mitk::Surface::New(); QString command; QString varName = QString::fromStdString( stdvarName ); command.append( QString("%1_addr_str = %1.GetAddressAsString(\"vtkPolyData\")\n").arg(varName) ); // remove 0x from the address command.append( QString("%1_addr = int(%1_addr_str[5:],16)").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); // get address of the object pyAddr = PyDict_GetItemString(pyDict,QString("%1_addr").arg(varName).toStdString().c_str()); // convert to long addr = PyInt_AsLong(pyAddr); MITK_DEBUG << "Python object address: " << addr; // get the object vtkPolyData* poly = (vtkPolyData*)((void*)addr); surface->SetVtkPolyData(poly); // delete helper variables from python stack command = ""; command.append( QString("del %1_addr_str\n").arg(varName) ); command.append( QString("del %1_addr").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return surface; } bool mitk::PythonService::CopyToPythonAsVtkPolyData( mitk::Surface* surface, const std::string& stdvarName ) { QString varName = QString::fromStdString( stdvarName ); std::ostringstream oss; std::string addr = ""; QString command; QString address; oss << (void*) ( surface->GetVtkPolyData() ); // get the address addr = oss.str(); // remove "0x" address = QString::fromStdString(addr.substr(2)); command.append( QString("%1 = vtk.vtkPolyData(\"%2\")\n").arg(varName).arg(address) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return true; } bool mitk::PythonService::IsSimpleItkPythonWrappingAvailable() { this->Execute( "import SimpleITK as sitk\n", IPythonService::SINGLE_LINE_COMMAND ); // directly access cpp lib this->Execute( "import SimpleITK._SimpleITK as _SimpleITK\n", IPythonService::SINGLE_LINE_COMMAND ); m_ItkWrappingAvailable = !this->PythonErrorOccured(); // check for numpy this->Execute( "import numpy\n", IPythonService::SINGLE_LINE_COMMAND ); if ( this->PythonErrorOccured() ) MITK_ERROR << "Numpy not found."; m_ItkWrappingAvailable = !this->PythonErrorOccured(); return m_ItkWrappingAvailable; } bool mitk::PythonService::IsOpenCvPythonWrappingAvailable() { this->Execute( "import cv2\n", IPythonService::SINGLE_LINE_COMMAND ); m_OpenCVWrappingAvailable = !this->PythonErrorOccured(); return m_OpenCVWrappingAvailable; } bool mitk::PythonService::IsVtkPythonWrappingAvailable() { this->Execute( "import vtk", IPythonService::SINGLE_LINE_COMMAND ); //this->Execute( "print \"Using VTK version \" + vtk.vtkVersion.GetVTKVersion()\n", IPythonService::SINGLE_LINE_COMMAND ); m_VtkWrappingAvailable = !this->PythonErrorOccured(); return m_VtkWrappingAvailable; } bool mitk::PythonService::PythonErrorOccured() const { return m_ErrorOccured; } diff --git a/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp b/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp index 65be0f3e67..1bbfcf7867 100644 --- a/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp +++ b/Modules/RT/src/mitkDoseImageVtkMapper2D.cpp @@ -1,1163 +1,1163 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkImageStatisticsHolder.h" #include "mitkPlaneClipping.h" #include "mitkPropertyNameHelper.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // MITK Rendering #include "mitkDoseImageVtkMapper2D.h" #include "vtkMitkLevelWindowFilter.h" #include "vtkMitkThickSlicesFilter.h" #include "vtkNeverTranslucentTexture.h" // VTK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // ITK #include mitk::DoseImageVtkMapper2D::DoseImageVtkMapper2D() { } mitk::DoseImageVtkMapper2D::~DoseImageVtkMapper2D() { // The 3D RW Mapper (PlaneGeometryDataVtkMapper3D) is listening to this event, // in order to delete the images from the 3D RW. this->InvokeEvent(itk::DeleteEvent()); } // set the two points defining the textured plane according to the dimension and spacing void mitk::DoseImageVtkMapper2D::GeneratePlane(mitk::BaseRenderer *renderer, double planeBounds[6]) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); float depth = this->CalculateLayerDepth(renderer); // Set the origin to (xMin; yMin; depth) of the plane. This is necessary for obtaining the correct // plane size in crosshair rotation and swivel mode. localStorage->m_Plane->SetOrigin(planeBounds[0], planeBounds[2], depth); // These two points define the axes of the plane in combination with the origin. // Point 1 is the x-axis and point 2 the y-axis. // Each plane is transformed according to the view (axial, coronal and saggital) afterwards. localStorage->m_Plane->SetPoint1(planeBounds[1], planeBounds[2], depth); // P1: (xMax, yMin, depth) localStorage->m_Plane->SetPoint2(planeBounds[0], planeBounds[3], depth); // P2: (xMin, yMax, depth) } float mitk::DoseImageVtkMapper2D::CalculateLayerDepth(mitk::BaseRenderer *renderer) { // get the clipping range to check how deep into z direction we can render images double maxRange = renderer->GetVtkRenderer()->GetActiveCamera()->GetClippingRange()[1]; // Due to a VTK bug, we cannot use the whole clipping range. /100 is empirically determined float depth = -maxRange * 0.01; // divide by 100 int layer = 0; GetDataNode()->GetIntProperty("layer", layer, renderer); // add the layer property for each image to render images with a higher layer on top of the others depth += layer * 10; //*10: keep some room for each image (e.g. for ODFs in between) if (depth > 0.0f) { depth = 0.0f; MITK_WARN << "Layer value exceeds clipping range. Set to minimum instead."; } return depth; } const mitk::Image *mitk::DoseImageVtkMapper2D::GetInput(void) { return static_cast(GetDataNode()->GetData()); } vtkProp *mitk::DoseImageVtkMapper2D::GetVtkProp(mitk::BaseRenderer *renderer) { // return the actor corresponding to the renderer return m_LSH.GetLocalStorage(renderer)->m_Actors; } void mitk::DoseImageVtkMapper2D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); mitk::Image *input = const_cast(this->GetInput()); mitk::DataNode *datanode = this->GetDataNode(); if (input == nullptr || input->IsInitialized() == false) { return; } // check if there is a valid worldGeometry const PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if ((worldGeometry == nullptr) || (!worldGeometry->IsValid()) || (!worldGeometry->HasReferenceGeometry())) { return; } input->Update(); // early out if there is no intersection of the current rendering geometry // and the geometry of the image that is to be rendered. if (!RenderingGeometryIntersectsImage(worldGeometry, input->GetSlicedGeometry())) { // set image to nullptr, to clear the texture in 3D, because // the latest image is used there if the plane is out of the geometry // see bug-13275 localStorage->m_ReslicedImage = nullptr; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); return; } // set main input for ExtractSliceFilter localStorage->m_Reslicer->SetInput(input); localStorage->m_Reslicer->SetWorldGeometry(worldGeometry); localStorage->m_Reslicer->SetTimeStep(this->GetTimestep()); // set the transformation of the image to adapt reslice axis localStorage->m_Reslicer->SetResliceTransformByGeometry( input->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())); // is the geometry of the slice based on the input image or the worldgeometry? bool inPlaneResampleExtentByGeometry = false; datanode->GetBoolProperty("in plane resample extent by geometry", inPlaneResampleExtentByGeometry, renderer); localStorage->m_Reslicer->SetInPlaneResampleExtentByGeometry(inPlaneResampleExtentByGeometry); // Initialize the interpolation mode for resampling; switch to nearest // neighbor if the input image is too small. if ((input->GetDimension() >= 3) && (input->GetDimension(2) > 1)) { VtkResliceInterpolationProperty *resliceInterpolationProperty; datanode->GetProperty(resliceInterpolationProperty, "reslice interpolation"); int interpolationMode = VTK_RESLICE_NEAREST; if (resliceInterpolationProperty != nullptr) { interpolationMode = resliceInterpolationProperty->GetInterpolation(); } switch (interpolationMode) { case VTK_RESLICE_NEAREST: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); break; case VTK_RESLICE_LINEAR: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_LINEAR); break; case VTK_RESLICE_CUBIC: localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_CUBIC); break; } } else { localStorage->m_Reslicer->SetInterpolationMode(ExtractSliceFilter::RESLICE_NEAREST); } // set the vtk output property to true, makes sure that no unneeded mitk image convertion // is done. localStorage->m_Reslicer->SetVtkOutputRequest(true); // Thickslicing int thickSlicesMode = 0; int thickSlicesNum = 1; // Thick slices parameters if (input->GetPixelType().GetNumberOfComponents() == 1) // for now only single component are allowed { DataNode *dn = renderer->GetCurrentWorldPlaneGeometryNode(); if (dn) { ResliceMethodProperty *resliceMethodEnumProperty = nullptr; if (dn->GetProperty(resliceMethodEnumProperty, "reslice.thickslices") && resliceMethodEnumProperty) thickSlicesMode = resliceMethodEnumProperty->GetValueAsId(); IntProperty *intProperty = nullptr; if (dn->GetProperty(intProperty, "reslice.thickslices.num") && intProperty) { thickSlicesNum = intProperty->GetValue(); if (thickSlicesNum < 1) thickSlicesNum = 1; if (thickSlicesNum > 10) thickSlicesNum = 10; } } else { MITK_WARN << "no associated widget plane data tree node found"; } } const PlaneGeometry *planeGeometry = dynamic_cast(worldGeometry); if (thickSlicesMode > 0) { double dataZSpacing = 1.0; Vector3D normInIndex, normal; if (planeGeometry != nullptr) { normal = planeGeometry->GetNormal(); } else { const mitk::AbstractTransformGeometry *abstractGeometry = dynamic_cast(worldGeometry); if (abstractGeometry != nullptr) normal = abstractGeometry->GetPlane()->GetNormal(); else return; // no fitting geometry set } normal.Normalize(); input->GetTimeGeometry()->GetGeometryForTimeStep(this->GetTimestep())->WorldToIndex(normal, normInIndex); dataZSpacing = 1.0 / normInIndex.GetNorm(); localStorage->m_Reslicer->SetOutputDimensionality(3); localStorage->m_Reslicer->SetOutputSpacingZDirection(dataZSpacing); localStorage->m_Reslicer->SetOutputExtentZDirection(-thickSlicesNum, 0 + thickSlicesNum); // Do the reslicing. Modified() is called to make sure that the reslicer is // executed even though the input geometry information did not change; this // is necessary when the input /em data, but not the /em geometry changes. localStorage->m_TSFilter->SetThickSliceMode(thickSlicesMode - 1); localStorage->m_TSFilter->SetInputData(localStorage->m_Reslicer->GetVtkOutput()); // vtkFilter=>mitkFilter=>vtkFilter update mechanism will fail without calling manually localStorage->m_Reslicer->Modified(); localStorage->m_Reslicer->Update(); localStorage->m_TSFilter->Modified(); localStorage->m_TSFilter->Update(); localStorage->m_ReslicedImage = localStorage->m_TSFilter->GetOutput(); } else { // this is needed when thick mode was enable bevore. These variable have to be reset to default values localStorage->m_Reslicer->SetOutputDimensionality(2); localStorage->m_Reslicer->SetOutputSpacingZDirection(1.0); localStorage->m_Reslicer->SetOutputExtentZDirection(0, 0); localStorage->m_Reslicer->Modified(); // start the pipeline with updating the largest possible, needed if the geometry of the input has changed localStorage->m_Reslicer->UpdateLargestPossibleRegion(); localStorage->m_ReslicedImage = localStorage->m_Reslicer->GetVtkOutput(); } // Bounds information for reslicing (only reuqired if reference geometry // is present) // this used for generating a vtkPLaneSource with the right size double sliceBounds[6]; for (int i = 0; i < 6; ++i) { sliceBounds[i] = 0.0; } localStorage->m_Reslicer->GetClippedPlaneBounds(sliceBounds); // get the spacing of the slice localStorage->m_mmPerPixel = localStorage->m_Reslicer->GetOutputSpacing(); // calculate minimum bounding rect of IMAGE in texture { double textureClippingBounds[6]; for (int i = 0; i < 6; ++i) { textureClippingBounds[i] = 0.0; } // Calculate the actual bounds of the transformed plane clipped by the // dataset bounding box; this is required for drawing the texture at the // correct position during 3D mapping. mitk::PlaneClipping::CalculateClippedPlaneBounds(input->GetGeometry(), planeGeometry, textureClippingBounds); textureClippingBounds[0] = static_cast(textureClippingBounds[0] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[1] = static_cast(textureClippingBounds[1] / localStorage->m_mmPerPixel[0] + 0.5); textureClippingBounds[2] = static_cast(textureClippingBounds[2] / localStorage->m_mmPerPixel[1] + 0.5); textureClippingBounds[3] = static_cast(textureClippingBounds[3] / localStorage->m_mmPerPixel[1] + 0.5); // clipping bounds for cutting the image localStorage->m_LevelWindowFilter->SetClippingBounds(textureClippingBounds); } // get the number of scalar components to distinguish between different image types int numberOfComponents = localStorage->m_ReslicedImage->GetNumberOfScalarComponents(); // get the showIsoLines property bool showIsoLines = false; datanode->GetBoolProperty("dose.showIsoLines", showIsoLines, renderer); if (showIsoLines) // contour rendering { // generate contours/outlines localStorage->m_OutlinePolyData = CreateOutlinePolyData(renderer); float binaryOutlineWidth(1.0); if (datanode->GetFloatProperty("outline width", binaryOutlineWidth, renderer)) { if (localStorage->m_Actors->GetNumberOfPaths() > 1) { float binaryOutlineShadowWidth(1.5); datanode->GetFloatProperty("outline shadow width", binaryOutlineShadowWidth, renderer); dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetLineWidth(binaryOutlineWidth * binaryOutlineShadowWidth); } localStorage->m_Actor->GetProperty()->SetLineWidth(binaryOutlineWidth); } } else { localStorage->m_ReslicedImage = nullptr; localStorage->m_Mapper->SetInputData(localStorage->m_EmptyPolyData); return; } this->ApplyOpacity(renderer); this->ApplyRenderingMode(renderer); // do not use a VTK lookup table (we do that ourselves in m_LevelWindowFilter) localStorage->m_Texture->SetColorModeToDirectScalars(); int displayedComponent = 0; if (datanode->GetIntProperty("Image.Displayed Component", displayedComponent, renderer) && numberOfComponents > 1) { localStorage->m_VectorComponentExtractor->SetComponents(displayedComponent); localStorage->m_VectorComponentExtractor->SetInputData(localStorage->m_ReslicedImage); localStorage->m_LevelWindowFilter->SetInputConnection(localStorage->m_VectorComponentExtractor->GetOutputPort(0)); } else { // connect the input with the levelwindow filter localStorage->m_LevelWindowFilter->SetInputData(localStorage->m_ReslicedImage); } // check for texture interpolation property bool textureInterpolation = false; GetDataNode()->GetBoolProperty("texture interpolation", textureInterpolation, renderer); // set the interpolation modus according to the property localStorage->m_Texture->SetInterpolate(textureInterpolation); // connect the texture with the output of the levelwindow filter localStorage->m_Texture->SetInputConnection(localStorage->m_LevelWindowFilter->GetOutputPort()); this->TransformActor(renderer); vtkActor *contourShadowActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); if (showIsoLines) // connect the mapper with the polyData which contains the lines { // We need the contour for the binary outline property as actor localStorage->m_Mapper->SetInputData(localStorage->m_OutlinePolyData); localStorage->m_Actor->SetTexture(nullptr); // no texture for contours bool binaryOutlineShadow(false); datanode->GetBoolProperty("outline binary shadow", binaryOutlineShadow, renderer); if (binaryOutlineShadow) contourShadowActor->SetVisibility(true); else contourShadowActor->SetVisibility(false); } else { // Connect the mapper with the input texture. This is the standard case. // setup the textured plane this->GeneratePlane(renderer, sliceBounds); // set the plane as input for the mapper localStorage->m_Mapper->SetInputConnection(localStorage->m_Plane->GetOutputPort()); // set the texture for the actor localStorage->m_Actor->SetTexture(localStorage->m_Texture); contourShadowActor->SetVisibility(false); } // We have been modified => save this for next Update() localStorage->m_LastUpdateTime.Modified(); } void mitk::DoseImageVtkMapper2D::ApplyLevelWindow(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); LevelWindow levelWindow; this->GetDataNode()->GetLevelWindow(levelWindow, renderer, "levelwindow"); localStorage->m_LevelWindowFilter->GetLookupTable()->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); mitk::LevelWindow opacLevelWindow; if (this->GetDataNode()->GetLevelWindow(opacLevelWindow, renderer, "opaclevelwindow")) { // pass the opaque level window to the filter localStorage->m_LevelWindowFilter->SetMinOpacity(opacLevelWindow.GetLowerWindowBound()); localStorage->m_LevelWindowFilter->SetMaxOpacity(opacLevelWindow.GetUpperWindowBound()); } else { // no opaque level window localStorage->m_LevelWindowFilter->SetMinOpacity(0.0); localStorage->m_LevelWindowFilter->SetMaxOpacity(255.0); } } void mitk::DoseImageVtkMapper2D::ApplyColor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float rgb[3] = {1.0f, 1.0f, 1.0f}; // check for color prop and use it for rendering if it exists // binary image hovering & binary image selection bool hover = false; bool selected = false; GetDataNode()->GetBoolProperty("binaryimage.ishovering", hover, renderer); GetDataNode()->GetBoolProperty("selected", selected, renderer); if (hover && !selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.hoveringcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (selected) { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("binaryimage.selectedcolor", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } else { GetDataNode()->GetColor(rgb, renderer, "color"); } } if (!hover && !selected) { GetDataNode()->GetColor(rgb, renderer, "color"); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); localStorage->m_Actor->GetProperty()->SetColor(rgbConv); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { float rgb[3] = {1.0f, 1.0f, 1.0f}; mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetDataNode()->GetProperty("outline binary shadow color", renderer)); if (colorprop.IsNotNull()) { memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); } double rgbConv[3] = {(double)rgb[0], (double)rgb[1], (double)rgb[2]}; // conversion to double for VTK dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0))->GetProperty()->SetColor(rgbConv); } } void mitk::DoseImageVtkMapper2D::ApplyOpacity(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = this->GetLocalStorage(renderer); float opacity = 1.0f; // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(opacity, renderer, "opacity"); // set the opacity according to the properties localStorage->m_Actor->GetProperty()->SetOpacity(opacity); if (localStorage->m_Actors->GetParts()->GetNumberOfItems() > 1) { dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)) ->GetProperty() ->SetOpacity(opacity); } } void mitk::DoseImageVtkMapper2D::ApplyRenderingMode(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); bool binary = false; this->GetDataNode()->GetBoolProperty("binary", binary, renderer); if (binary) // is it a binary image? { // for binary images, we always use our default LuT and map every value to (0,1) // the opacity of 0 will always be 0.0. We never a apply a LuT/TfF nor a level window. localStorage->m_LevelWindowFilter->SetLookupTable(localStorage->m_BinaryLookupTable); } else { // all other image types can make use of the rendering mode int renderingMode = mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR; mitk::RenderingModeProperty::Pointer mode = dynamic_cast(this->GetDataNode()->GetProperty("Image Rendering.Mode", renderer)); if (mode.IsNotNull()) { renderingMode = mode->GetRenderingMode(); } switch (renderingMode) { case mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_LookupTable_Color"; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LevelWindow_ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); this->ApplyLevelWindow(renderer); break; case mitk::RenderingModeProperty::LOOKUPTABLE_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = LookupTable_Color"; this->ApplyLookuptable(renderer); break; case mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR: MITK_DEBUG << "'Image Rendering.Mode' = ColorTransferFunction_Color"; this->ApplyColorTransferFunction(renderer); break; default: MITK_ERROR << "No valid 'Image Rendering.Mode' set. Using LOOKUPTABLE_LEVELWINDOW_COLOR instead."; this->ApplyLookuptable(renderer); this->ApplyLevelWindow(renderer); break; } } // we apply color for all images (including binaries). this->ApplyColor(renderer); } void mitk::DoseImageVtkMapper2D::ApplyLookuptable(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); vtkLookupTable *usedLookupTable = localStorage->m_ColorLookupTable; // If lookup table or transferfunction use is requested... mitk::LookupTableProperty::Pointer lookupTableProp = dynamic_cast(this->GetDataNode()->GetProperty("LookupTable")); if (lookupTableProp.IsNotNull()) // is a lookuptable set? { usedLookupTable = lookupTableProp->GetLookupTable()->GetVtkLookupTable(); } else { //"Image Rendering.Mode was set to use a lookup table but there is no property 'LookupTable'. // A default (rainbow) lookup table will be used. // Here have to do nothing. Warning for the user has been removed, due to unwanted console output // in every interation of the rendering. } localStorage->m_LevelWindowFilter->SetLookupTable(usedLookupTable); } void mitk::DoseImageVtkMapper2D::ApplyColorTransferFunction(mitk::BaseRenderer *renderer) { mitk::TransferFunctionProperty::Pointer transferFunctionProp = dynamic_cast( this->GetDataNode()->GetProperty("Image Rendering.Transfer Function", renderer)); if (transferFunctionProp.IsNull()) { MITK_ERROR << "'Image Rendering.Mode'' was set to use a color transfer function but there is no property 'Image " "Rendering.Transfer Function'. Nothing will be done."; return; } LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // pass the transfer function to our level window filter localStorage->m_LevelWindowFilter->SetLookupTable(transferFunctionProp->GetValue()->GetColorTransferFunction()); } void mitk::DoseImageVtkMapper2D::Update(mitk::BaseRenderer *renderer) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { return; } mitk::Image *data = const_cast(this->GetInput()); if (data == nullptr) { return; } // Calculate time step of the input data for the specified renderer (integer value) this->CalculateTimeStep(renderer); // Check if time step is valid const TimeGeometry *dataTimeGeometry = data->GetTimeGeometry(); if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) { return; } const DataNode *node = this->GetDataNode(); data->UpdateOutputInformation(); LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // check if something important has changed and we need to rerender if ((localStorage->m_LastUpdateTime < node->GetMTime()) // was the node modified? || (localStorage->m_LastUpdateTime < data->GetPipelineMTime()) // Was the data modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) // was the geometry modified? || (localStorage->m_LastUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || (localStorage->m_LastUpdateTime < node->GetPropertyList()->GetMTime()) // was a property modified? || (localStorage->m_LastUpdateTime < node->GetPropertyList(renderer)->GetMTime())) { this->GenerateDataForRenderer(renderer); } // since we have checked that nothing important has changed, we can set // m_LastUpdateTime to the current time localStorage->m_LastUpdateTime.Modified(); } void mitk::DoseImageVtkMapper2D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { mitk::Image::Pointer image = dynamic_cast(node->GetData()); // Properties common for both images and segmentations node->AddProperty("depthOffset", mitk::FloatProperty::New(0.0), renderer, overwrite); node->AddProperty("outline binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline width", mitk::FloatProperty::New(1.0), renderer, overwrite); node->AddProperty("outline binary shadow", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("outline binary shadow color", ColorProperty::New(0.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("outline shadow width", mitk::FloatProperty::New(1.5), renderer, overwrite); if (image->IsRotated()) node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_CUBIC)); else node->AddProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New()); node->AddProperty("texture interpolation", mitk::BoolProperty::New(false)); // default value node->AddProperty("in plane resample extent by geometry", mitk::BoolProperty::New(false)); node->AddProperty("bounding box", mitk::BoolProperty::New(false)); mitk::RenderingModeProperty::Pointer renderingModeProperty = mitk::RenderingModeProperty::New(); node->AddProperty("Image Rendering.Mode", renderingModeProperty); // Set default grayscale look-up table mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); mitkLut->SetType(mitk::LookupTable::GRAYSCALE); mitk::LookupTableProperty::Pointer mitkLutProp = mitk::LookupTableProperty::New(); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); std::string photometricInterpretation; // DICOM tag telling us how pixel values should be displayed if (node->GetStringProperty("dicom.pixel.PhotometricInterpretation", photometricInterpretation)) { // modality provided by DICOM or other reader if (photometricInterpretation.find("MONOCHROME1") != std::string::npos) // meaning: display MINIMUM pixels as WHITE { // Set inverse grayscale look-up table mitkLut->SetType(mitk::LookupTable::INVERSE_GRAYSCALE); mitkLutProp->SetLookupTable(mitkLut); node->SetProperty("LookupTable", mitkLutProp); } // Otherwise do nothing - the default grayscale look-up table has already been set } bool isBinaryImage(false); if (!node->GetBoolProperty("binary", isBinaryImage)) { // ok, property is not set, use heuristic to determine if this // is a binary image mitk::Image::Pointer centralSliceImage; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); centralSliceImage = sliceSelector->GetOutput(); if (centralSliceImage.IsNotNull() && centralSliceImage->IsInitialized()) { minValue = centralSliceImage->GetStatistics()->GetScalarValueMin(); maxValue = centralSliceImage->GetStatistics()->GetScalarValueMax(); min2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMin(); max2ndValue = centralSliceImage->GetStatistics()->GetScalarValue2ndMax(); } if ((maxValue == min2ndValue && minValue == max2ndValue) || minValue == maxValue) { // centralSlice is strange, lets look at all data minValue = image->GetStatistics()->GetScalarValueMin(); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } isBinaryImage = (maxValue == min2ndValue && minValue == max2ndValue); } // some more properties specific for a binary... if (isBinaryImage) { node->AddProperty("opacity", mitk::FloatProperty::New(0.3f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.selectedannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binaryimage.hoveringannotationcolor", ColorProperty::New(1.0, 0.0, 0.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(true), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(10), renderer, overwrite); } else //...or image type object { node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, overwrite); node->AddProperty("color", ColorProperty::New(1.0, 1.0, 1.0), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); node->AddProperty("layer", mitk::IntProperty::New(0), renderer, overwrite); std::string className = image->GetNameOfClass(); if (className != "TensorImage" && className != "OdfImage" && className != "ShImage") { PixelType pixelType = image->GetPixelType(); size_t numComponents = pixelType.GetNumberOfComponents(); if ((pixelType.GetPixelTypeAsString() == "vector" && numComponents > 1) || numComponents == 2 || numComponents > 4) node->AddProperty("Image.Displayed Component", mitk::IntProperty::New(0), renderer, overwrite); } } if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) { /* initialize level/window from DICOM tags */ std::string sLevel; std::string sWindow; if (GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1050, "dicom.voilut.WindowCenter", image->GetPropertyList(), sLevel) && GetBackwardsCompatibleDICOMProperty( 0x0028, 0x1051, "dicom.voilut.WindowWidth", image->GetPropertyList(), sWindow)) { float level = atof(sLevel.c_str()); float window = atof(sWindow.c_str()); mitk::LevelWindow contrast; std::string sSmallestPixelValueInSeries; std::string sLargestPixelValueInSeries; if (GetBackwardsCompatibleDICOMProperty(0x0028, 0x0108, "dicom.series.SmallestPixelValueInSeries", image->GetPropertyList(), sSmallestPixelValueInSeries) && GetBackwardsCompatibleDICOMProperty(0x0028, 0x0109, "dicom.series.LargestPixelValueInSeries", image->GetPropertyList(), sLargestPixelValueInSeries)) { float smallestPixelValueInSeries = atof(sSmallestPixelValueInSeries.c_str()); float largestPixelValueInSeries = atof(sLargestPixelValueInSeries.c_str()); contrast.SetRangeMinMax(smallestPixelValueInSeries - 1, largestPixelValueInSeries + 1); // why not a little buffer? // might remedy some l/w widget challenges } else { contrast.SetAuto(static_cast(node->GetData()), false, true); // we need this as a fallback } contrast.SetLevelWindow(level, window, true); node->SetProperty("levelwindow", LevelWindowProperty::New(contrast), renderer); } } if (((overwrite) || (node->GetProperty("opaclevelwindow", renderer) == nullptr)) && - (image->GetPixelType().GetPixelType() == itk::ImageIOBase::RGBA) && - (image->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR)) + (image->GetPixelType().GetPixelType() == itk::IOPixelEnum::RGBA) && + (image->GetPixelType().GetComponentType() == itk::IOComponentEnum::UCHAR)) { mitk::LevelWindow opaclevwin; opaclevwin.SetRangeMinMax(0, 255); opaclevwin.SetWindowBounds(0, 255); mitk::LevelWindowProperty::Pointer prop = mitk::LevelWindowProperty::New(opaclevwin); node->SetProperty("opaclevelwindow", prop, renderer); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } mitk::DoseImageVtkMapper2D::LocalStorage *mitk::DoseImageVtkMapper2D::GetLocalStorage(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer); } vtkSmartPointer mitk::DoseImageVtkMapper2D::CreateOutlinePolyData(mitk::BaseRenderer *renderer) { vtkSmartPointer points = vtkSmartPointer::New(); // the points to draw vtkSmartPointer lines = vtkSmartPointer::New(); // the lines to connect the points vtkSmartPointer colors = vtkSmartPointer::New(); colors->SetNumberOfComponents(3); colors->SetName("Colors"); float pref; this->GetDataNode()->GetFloatProperty(mitk::RTConstants::REFERENCE_DOSE_PROPERTY_NAME.c_str(), pref); mitk::IsoDoseLevelSetProperty::Pointer propIsoSet = dynamic_cast( GetDataNode()->GetProperty(mitk::RTConstants::DOSE_ISO_LEVELS_PROPERTY_NAME.c_str())); mitk::IsoDoseLevelSet::Pointer isoDoseLevelSet = propIsoSet->GetValue(); for (mitk::IsoDoseLevelSet::ConstIterator doseIT = isoDoseLevelSet->Begin(); doseIT != isoDoseLevelSet->End(); ++doseIT) { if (doseIT->GetVisibleIsoLine()) { this->CreateLevelOutline(renderer, &(doseIT.Value()), pref, points, lines, colors); } // end of if visible dose value } // end of loop over all does values mitk::IsoDoseLevelVectorProperty::Pointer propfreeIsoVec = dynamic_cast( GetDataNode()->GetProperty(mitk::RTConstants::DOSE_FREE_ISO_VALUES_PROPERTY_NAME.c_str())); mitk::IsoDoseLevelVector::Pointer frereIsoDoseLevelVec = propfreeIsoVec->GetValue(); for (mitk::IsoDoseLevelVector::ConstIterator freeDoseIT = frereIsoDoseLevelVec->Begin(); freeDoseIT != frereIsoDoseLevelVec->End(); ++freeDoseIT) { if (freeDoseIT->Value()->GetVisibleIsoLine()) { this->CreateLevelOutline(renderer, freeDoseIT->Value(), pref, points, lines, colors); } // end of if visible dose value } // end of loop over all does values // Create a polydata to store everything in vtkSmartPointer polyData = vtkSmartPointer::New(); // Add the points to the dataset polyData->SetPoints(points); // Add the lines to the dataset polyData->SetLines(lines); polyData->GetCellData()->SetScalars(colors); return polyData; } void mitk::DoseImageVtkMapper2D::CreateLevelOutline(mitk::BaseRenderer *renderer, const mitk::IsoDoseLevel *level, float pref, vtkSmartPointer points, vtkSmartPointer lines, vtkSmartPointer colors) { LocalStorage *localStorage = this->GetLocalStorage(renderer); // get the min and max index values of each direction int *extent = localStorage->m_ReslicedImage->GetExtent(); int xMin = extent[0]; int xMax = extent[1]; int yMin = extent[2]; int yMax = extent[3]; int *dims = localStorage->m_ReslicedImage->GetDimensions(); // dimensions of the image int line = dims[0]; // how many pixels per line? // get the depth for each contour float depth = CalculateLayerDepth(renderer); double doseValue = level->GetDoseValue() * pref; mitk::IsoDoseLevel::ColorType isoColor = level->GetColor(); unsigned char colorLine[3] = {static_cast(isoColor.GetRed() * 255), static_cast(isoColor.GetGreen() * 255), static_cast(isoColor.GetBlue() * 255)}; int x = xMin; // pixel index x int y = yMin; // pixel index y float *currentPixel; // We take the pointer to the first pixel of the image currentPixel = static_cast(localStorage->m_ReslicedImage->GetScalarPointer()); if (!currentPixel){ mitkThrow() << "currentPixel invalid"; } while (y <= yMax) { // if the current pixel value is set to something if ((currentPixel) && (*currentPixel >= doseValue)) { // check in which direction a line is necessary // a line is added if the neighbor of the current pixel has the value 0 // and if the pixel is located at the edge of the image // if vvvvv not the first line vvvvv if (y > yMin && *(currentPixel - line) < doseValue) { // x direction - bottom edge of the pixel // add the 2 points vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); // add the line between both points lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the last line vvvvv if (y < yMax && *(currentPixel + line) < doseValue) { // x direction - top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the first pixel vvvvv if ((x > xMin || y > yMin) && *(currentPixel - 1) < doseValue) { // y direction - left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv not the last pixel vvvvv if ((y < yMax || (x < xMax)) && *(currentPixel + 1) < doseValue) { // y direction - right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } /* now consider pixels at the edge of the image */ // if vvvvv left edge of image vvvvv if (x == xMin) { // draw left edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv right edge of image vvvvv if (x == xMax) { // draw right edge of the pixel vtkIdType p1 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv bottom edge of image vvvvv if (y == yMin) { // draw bottom edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint((x + 1) * localStorage->m_mmPerPixel[0], y * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } // if vvvvv top edge of image vvvvv if (y == yMax) { // draw top edge of the pixel vtkIdType p1 = points->InsertNextPoint(x * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); vtkIdType p2 = points->InsertNextPoint( (x + 1) * localStorage->m_mmPerPixel[0], (y + 1) * localStorage->m_mmPerPixel[1], depth); lines->InsertNextCell(2); lines->InsertCellPoint(p1); lines->InsertCellPoint(p2); colors->InsertNextTypedTuple(colorLine); } } // end if currentpixel is set x++; if (x > xMax) { // reached end of line x = xMin; y++; } // Increase the pointer-position to the next pixel. // This is safe, as the while-loop and the x-reset logic above makes // sure we do not exceed the bounds of the image currentPixel++; } // end of while } void mitk::DoseImageVtkMapper2D::TransformActor(mitk::BaseRenderer *renderer) { LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); // get the transformation matrix of the reslicer in order to render the slice as axial, coronal or saggital vtkSmartPointer trans = vtkSmartPointer::New(); vtkSmartPointer matrix = localStorage->m_Reslicer->GetResliceAxes(); trans->SetMatrix(matrix); // transform the plane/contour (the actual actor) to the corresponding view (axial, coronal or saggital) localStorage->m_Actor->SetUserTransform(trans); // transform the origin to center based coordinates, because MITK is center based. localStorage->m_Actor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); if (localStorage->m_Actors->GetNumberOfPaths() > 1) { vtkActor *secondaryActor = dynamic_cast(localStorage->m_Actors->GetParts()->GetItemAsObject(0)); secondaryActor->SetUserTransform(trans); secondaryActor->SetPosition(-0.5 * localStorage->m_mmPerPixel[0], -0.5 * localStorage->m_mmPerPixel[1], 0.0); } } bool mitk::DoseImageVtkMapper2D::RenderingGeometryIntersectsImage(const PlaneGeometry *renderingGeometry, SlicedGeometry3D *imageGeometry) { // if either one of the two geometries is nullptr we return true // for safety reasons if (renderingGeometry == nullptr || imageGeometry == nullptr) return true; // get the distance for the first cornerpoint ScalarType initialDistance = renderingGeometry->SignedDistance(imageGeometry->GetCornerPoint(0)); for (int i = 1; i < 8; i++) { mitk::Point3D cornerPoint = imageGeometry->GetCornerPoint(i); // get the distance to the other cornerpoints ScalarType distance = renderingGeometry->SignedDistance(cornerPoint); // if it has not the same signing as the distance of the first point if (initialDistance * distance < 0) { // we have an intersection and return true return true; } } // all distances have the same sign, no intersection and we return false return false; } mitk::DoseImageVtkMapper2D::LocalStorage::~LocalStorage() { } mitk::DoseImageVtkMapper2D::LocalStorage::LocalStorage() : m_VectorComponentExtractor(vtkSmartPointer::New()) { m_LevelWindowFilter = vtkSmartPointer::New(); // Do as much actions as possible in here to avoid double executions. m_Plane = vtkSmartPointer::New(); m_Texture = vtkSmartPointer::New().GetPointer(); m_DefaultLookupTable = vtkSmartPointer::New(); m_BinaryLookupTable = vtkSmartPointer::New(); m_ColorLookupTable = vtkSmartPointer::New(); m_Mapper = vtkSmartPointer::New(); m_Actor = vtkSmartPointer::New(); m_Actors = vtkSmartPointer::New(); m_Reslicer = mitk::ExtractSliceFilter::New(); m_TSFilter = vtkSmartPointer::New(); m_OutlinePolyData = vtkSmartPointer::New(); m_ReslicedImage = vtkSmartPointer::New(); m_EmptyPolyData = vtkSmartPointer::New(); // the following actions are always the same and thus can be performed // in the constructor for each image (i.e. the image-corresponding local storage) m_TSFilter->ReleaseDataFlagOn(); mitk::LookupTable::Pointer mitkLUT = mitk::LookupTable::New(); // built a default lookuptable mitkLUT->SetType(mitk::LookupTable::GRAYSCALE); m_DefaultLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_BINARY); m_BinaryLookupTable = mitkLUT->GetVtkLookupTable(); mitkLUT->SetType(mitk::LookupTable::LEGACY_RAINBOW_COLOR); m_ColorLookupTable = mitkLUT->GetVtkLookupTable(); // do not repeat the texture (the image) m_Texture->RepeatOff(); // set the mapper for the actor m_Actor->SetMapper(m_Mapper); vtkSmartPointer outlineShadowActor = vtkSmartPointer::New(); outlineShadowActor->SetMapper(m_Mapper); m_Actors->AddPart(outlineShadowActor); m_Actors->AddPart(m_Actor); } diff --git a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h index a8580e38c1..fdcb418dca 100644 --- a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h +++ b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.h @@ -1,250 +1,250 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 __itkAdaptiveThresholdIterator_h #define __itkAdaptiveThresholdIterator_h #include "itkConditionalConstIterator.h" #include "itkImage.h" #include "itkIndex.h" #include "itkSize.h" #include #include #include #include namespace itk { /** * \class AdaptiveThresholdIterator * \brief Iterates over an image using a variable image function, * which threshold can be varied during the iteration process. * * \ingroup ImageIterators * */ template class ITK_EXPORT AdaptiveThresholdIterator : public ConditionalConstIterator { public: /** Standard class typedefs. */ typedef AdaptiveThresholdIterator Self; typedef ConditionalConstIterator Superclass; typedef TImage ImageType; // A temporary image used for storing info about all indices typedef TImage TTempImage; typename TTempImage::Pointer tempPtr; //[!] isn't really used?! /** Type of function */ typedef TFunction FunctionType; /** Type of vector used to store location info in the spatial function */ typedef typename TFunction::InputType FunctionInputType; /** Size typedef support. */ typedef typename TImage::SizeType SizeType; /** Region typedef support */ typedef typename TImage::RegionType RegionType; typedef typename TImage::IndexType IndexType; /** Internal Pixel Type */ typedef typename TImage::InternalPixelType InternalPixelType; /** External Pixel Type */ typedef typename TImage::PixelType PixelType; /** Queue containing indices representing a voxel position */ typedef std::queue IndexQueueType; /** Map used to generate the output result */ typedef std::map QueueMapType; /** Dimension of the image the iterator walks. This constant is needed so * that functions that are templated over image iterator type (as opposed to * being templated over pixel type and dimension) can have compile time * access to the dimension of the image that the iterator walks. */ itkStaticConstMacro(NDimensions, unsigned int, TImage::ImageDimension); /** Constructor establishes an iterator to walk a particular image and a * particular region of that image. This version of the constructor uses * an explicit seed pixel for the flood fill, the "startIndex" */ AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr, IndexType startIndex); /** Constructor establishes an iterator to walk a particular image and a * particular region of that image. This version of the constructor uses * an explicit list of seed pixels for the flood fill, the "startIndex" */ AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr, std::vector &startIndex); /** Constructor establishes an iterator to walk a particular image and a * particular region of that image. This version of the constructor * should be used when the seed pixel is unknown */ AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr); /** Default Destructor. */ ~AdaptiveThresholdIterator() override{}; /** Initializes the iterator, called from constructor */ void InitializeIterator(); // makes the iterator go one step further void DoExtendedFloodStep(); // set-method for member-variable void SetExpansionDirection(bool upwards); // Init-method void InitRegionGrowingState(); void SetMinTH(int min); void SetMaxTH(int max); int GetSeedPointValue(void); /** switch between fine and raw leakage detection */ void SetFineDetectionMode(bool fine = false) { m_FineDetectionMode = fine; m_DetectionStop = false; } /** Get the index. This provides a read only reference to the index. * This causes the index to be calculated from pointer arithmetic and is * therefore an expensive operation. * \sa SetIndex */ const IndexType GetIndex() override { return (*m_QueueMap.find(m_RegionGrowingState)).second.front(); } // [!] is never called?! const PixelType Get(void) const override { return const_cast(this->m_Image.GetPointer()) ->GetPixel((*m_QueueMap.find(m_RegionGrowingState)).second.front()); } //[!] is never called?! void Set(const PixelType &value) { const_cast(this->m_Image.GetPointer()) ->GetPixel((*m_QueueMap.find(m_RegionGrowingState)).second.front()) = value; } void GoToBegin(); /** Is the iterator at the end of the region? */ - bool IsAtEnd() override { return this->m_IsAtEnd; }; + bool IsAtEnd() const override { return this->m_IsAtEnd; }; /** Walk forward one index */ void operator++() override { this->DoExtendedFloodStep(); } virtual SmartPointer GetFunction() const { return m_Function; } /** operator= is provided to make sure the handle to the image is properly * reference counted. */ Self &operator=(const Self &it) { this->m_Image = it.m_Image; // copy the smart pointer this->m_Region = it.m_Region; this->m_InitializeValue = it.m_InitializeValue; this->m_RegionGrowingState = it.m_RegionGrowingState; this->m_MinTH = it.m_MinTH; this->m_MaxTH = it.m_MaxTH; this->m_SeedPointValue = it.m_SeedPointValue; this->m_VoxelCounter = it.m_VoxelCounter; this->m_LastVoxelNumber = it.m_LastVoxelNumber; this->m_DetectedLeakagePoint = it.m_DetectedLeakagePoint; this->m_CurrentLeakageRatio = it.m_CurrentLeakageRatio; return *this; } /** Compute whether the index of interest should be included in the flood */ bool IsPixelIncluded(const IndexType &index) const override; // Calculate the value the outputImage is initialized to static int CalculateInitializeValue(int lower, int upper) { return ((upper - lower) + 1) * (-1); }; int GetLeakagePoint(void) { return m_DetectedLeakagePoint; } protected: /* * @brief Pointer on the output image to which the result shall be written */ SmartPointer m_OutputImage; SmartPointer m_Function; /** A list of locations to start the recursive fill */ std::vector m_StartIndices; /** The origin of the source image */ typename ImageType::PointType m_ImageOrigin; /** The spacing of the source image */ typename ImageType::SpacingType m_ImageSpacing; /** Region of the source image */ RegionType m_ImageRegion; bool m_UpwardsExpansion; int m_InitializeValue; void ExpandThresholdUpwards(); void ExpandThresholdDownwards(); void IncrementRegionGrowingState(); // calculates how many steps the voxel is from the current step int EstimateDistance(IndexType); // calculates how many expansion steps will be taken unsigned int CalculateMaxRGS(); private: void InsertIndexTypeIntoQueueMap(unsigned int key, IndexType index); int m_RegionGrowingState; QueueMapType m_QueueMap; int m_MinTH; int m_MaxTH; int m_SeedPointValue; unsigned int m_VoxelCounter; unsigned int m_LastVoxelNumber; int m_DetectedLeakagePoint; float m_CurrentLeakageRatio; void CheckSeedPointValue(); /* flag for switching between raw leakage detection (bigger bronchial vessels) * and fine leakage detection (smaller bronchial vessels [starting from leaves]) */ bool m_FineDetectionMode; bool m_DetectionStop; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkAdaptiveThresholdIterator.txx" #endif #endif diff --git a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx index cf9855e799..aeb755b305 100644 --- a/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx +++ b/Modules/Segmentation/Algorithms/itkAdaptiveThresholdIterator.txx @@ -1,422 +1,422 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _itkAdaptiveThresholdIterator_txx #define _itkAdaptiveThresholdIterator_txx #include "itkAdaptiveThresholdIterator.h" #include "mitkProgressBar.h" #include namespace itk { template AdaptiveThresholdIterator::AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr, IndexType startIndex) : m_FineDetectionMode(false), m_DetectionStop(false) { this->m_OutputImage = imagePtr; m_Function = fnPtr; m_StartIndices.push_back(startIndex); // Set up the temporary image this->InitializeIterator(); } template AdaptiveThresholdIterator::AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr, std::vector &startIndex) : m_FineDetectionMode(false), m_DetectionStop(false) { this->m_OutputImage = imagePtr; m_Function = fnPtr; unsigned int i; for (i = 0; i < startIndex.size(); i++) { m_StartIndices.push_back(startIndex[i]); } // Set up the temporary image this->InitializeIterator(); } template AdaptiveThresholdIterator::AdaptiveThresholdIterator(ImageType *imagePtr, FunctionType *fnPtr) : m_FineDetectionMode(false), m_DetectionStop(false) { this->m_OutputImage = imagePtr; // here we store the image, we have to wite the result to m_Function = fnPtr; // Set up the temporary image this->InitializeIterator(); } template void AdaptiveThresholdIterator::InitializeIterator() { // Get the origin and spacing from the image in simple arrays m_ImageOrigin = this->m_OutputImage->GetOrigin(); m_ImageSpacing = this->m_OutputImage->GetSpacing(); m_ImageRegion = this->m_OutputImage->GetBufferedRegion(); this->InitRegionGrowingState(); m_VoxelCounter = 0; m_LastVoxelNumber = 0; m_CurrentLeakageRatio = 0; m_DetectedLeakagePoint = 0; } template void AdaptiveThresholdIterator::SetExpansionDirection(bool upwards) { m_UpwardsExpansion = upwards; } template void AdaptiveThresholdIterator::IncrementRegionGrowingState() { // make the progressbar go one step further if (!m_FineDetectionMode) mitk::ProgressBar::GetInstance()->Progress(); // updating the thresholds if (m_UpwardsExpansion) { this->ExpandThresholdUpwards(); } else { this->ExpandThresholdDownwards(); } // leakage-detection int criticalValue = 2000; // calculate a bit more "intelligent" if (!m_FineDetectionMode) { int diff = m_VoxelCounter - m_LastVoxelNumber; if (diff > m_CurrentLeakageRatio) { m_CurrentLeakageRatio = diff; m_DetectedLeakagePoint = m_RegionGrowingState; } m_LastVoxelNumber = m_VoxelCounter; m_VoxelCounter = 0; } else // fine leakage detection { // counting voxels over interations; if above a critical value (to be extended) then set this to leakage int diff = m_VoxelCounter - m_LastVoxelNumber; // std::cout<<"diff is: "< void AdaptiveThresholdIterator::ExpandThresholdUpwards() { int upper = (int)m_Function->GetUpper(); upper++; m_Function->ThresholdBetween(m_MinTH, upper); } template void AdaptiveThresholdIterator::ExpandThresholdDownwards() { int lower = (int)m_Function->GetLower(); lower--; m_Function->ThresholdBetween(lower, m_MaxTH); } template void AdaptiveThresholdIterator::InitRegionGrowingState() { this->m_RegionGrowingState = 1; } template int AdaptiveThresholdIterator::EstimateDistance(IndexType tempIndex) { PixelType value = this->m_Function->GetInputImage()->GetPixel(tempIndex); PixelType minPixel = PixelType(m_MinTH); PixelType maxPixel = PixelType(m_MaxTH); if (value > maxPixel || value < minPixel) { return 0; } if (m_UpwardsExpansion) { return (int)(value - m_SeedPointValue); } else { return (int)(m_SeedPointValue - value); } } template void AdaptiveThresholdIterator::SetMinTH(int min) { m_MinTH = min; } template void AdaptiveThresholdIterator::SetMaxTH(int max) { m_MaxTH = max; } template int AdaptiveThresholdIterator::GetSeedPointValue() { return this->m_SeedPointValue; } template void AdaptiveThresholdIterator::GoToBegin() { m_QueueMap.clear(); m_SeedPointValue = 0; IndexType seedIndex = m_StartIndices[0]; bool doAverage = false; // enable or disable manually! if (doAverage) { // loops for creating the sum of the N27-neighborhood around the seedpoint for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { for (int k = -1; k <= 1; k++) { seedIndex[0] = seedIndex[0] + i; seedIndex[1] = seedIndex[1] + j; seedIndex[2] = seedIndex[2] + k; m_SeedPointValue += (int)m_Function->GetInputImage()->GetPixel(seedIndex); } } } // value of seedpoint computed from mean of N27-neighborhood m_SeedPointValue = m_SeedPointValue / 27; } else { m_SeedPointValue = (int)m_Function->GetInputImage()->GetPixel(seedIndex); } this->CheckSeedPointValue(); m_InitializeValue = (this->CalculateMaxRGS() + 1); if (!m_FineDetectionMode) mitk::ProgressBar::GetInstance()->AddStepsToDo(m_InitializeValue - 1); // only initialize with zeros for the first segmention (raw segmentation mode) if (!m_FineDetectionMode) { this->m_OutputImage->FillBuffer((PixelType)0); } if (m_UpwardsExpansion) { m_Function->ThresholdBetween(m_MinTH, m_SeedPointValue); } else { m_Function->ThresholdBetween(m_SeedPointValue, m_MaxTH); } this->m_IsAtEnd = true; seedIndex = m_StartIndices[0]; // warum noch mal? Steht doch schon in Zeile 224 if (this->m_OutputImage->GetBufferedRegion().IsInside(seedIndex) && this->m_SeedPointValue >= this->m_MinTH && this->m_SeedPointValue <= this->m_MaxTH) { // Push the seed onto the queue this->InsertIndexTypeIntoQueueMap(m_RegionGrowingState, seedIndex); // Obviously, we're at the beginning this->m_IsAtEnd = false; this->m_OutputImage->SetPixel(seedIndex, (m_InitializeValue - m_RegionGrowingState)); } } template void AdaptiveThresholdIterator::CheckSeedPointValue() { // checks, if the value, that has been averaged over the N-27 neighborhood aorund the seedpoint is still inside // the thresholds-ranges. if not, the actual value of the seedpoint (not averaged) is used if (m_SeedPointValue < m_MinTH || m_SeedPointValue > m_MaxTH) { m_SeedPointValue = (int)m_Function->GetInputImage()->GetPixel(m_StartIndices[0]); } } template unsigned int AdaptiveThresholdIterator::CalculateMaxRGS() { if (m_UpwardsExpansion) { return (m_MaxTH - m_SeedPointValue); } else { return (m_SeedPointValue - m_MinTH); } } template bool AdaptiveThresholdIterator::IsPixelIncluded(const IndexType &index) const { // checks, if grayvalue of current voxel is inside the currently used thresholds return this->m_Function->EvaluateAtIndex(index); } template void AdaptiveThresholdIterator::InsertIndexTypeIntoQueueMap(unsigned int key, IndexType index) { // first check if the key-specific queue already exists if (m_QueueMap.count(key) == 0) { // if queue doesn´t exist, create it, push the IndexType onto it // and insert it into the map IndexQueueType newQueue; newQueue.push(index); typedef typename QueueMapType::value_type KeyIndexQueue; m_QueueMap.insert(KeyIndexQueue(key, newQueue)); } else { // if queue already exists in the map, push IndexType onto its specific queue (*(m_QueueMap.find(key))).second.push(index); } } template void AdaptiveThresholdIterator::DoExtendedFloodStep() { // The index in the front of the queue should always be // valid and be inside since this is what the iterator // uses in the Set/Get methods. This is ensured by the // GoToBegin() method. typename QueueMapType::iterator currentIt = m_QueueMap.find(m_RegionGrowingState); if (currentIt == m_QueueMap.end()) { this->IncrementRegionGrowingState(); } else { IndexQueueType *currentQueue = &(*currentIt).second; // Take the index in the front of the queue const IndexType &topIndex = currentQueue->front(); // Iterate through all possible dimensions // NOTE: Replace this with a ShapeNeighborhoodIterator for (unsigned int i = 0; i < NDimensions; i++) { // The j loop establishes either left or right neighbor (+-1) for (int j = -1; j <= 1; j += 2) { IndexType tempIndex; // build the index of a neighbor for (unsigned int k = 0; k < NDimensions; k++) { if (i != k) { - tempIndex.m_Index[k] = topIndex[k]; + tempIndex[k] = topIndex[k]; } else { - tempIndex.m_Index[k] = topIndex[k] + j; + tempIndex[k] = topIndex[k] + j; } } // end build the index of a neighbor // If this is a valid index and have not been tested, // then test it. if (m_ImageRegion.IsInside(tempIndex)) { // check if voxel hasn´t already been processed if (this->m_OutputImage->GetPixel(tempIndex) == 0) { // if it is inside, push it into the queue if (this->IsPixelIncluded(tempIndex)) { // hier wird Voxel in momentan aktiven Stack und ins OutputImage geschrieben this->InsertIndexTypeIntoQueueMap((m_RegionGrowingState), tempIndex); this->m_OutputImage->SetPixel(tempIndex, (m_InitializeValue - m_RegionGrowingState)); } else // If the pixel is not inside the current threshold { int distance = this->EstimateDistance( tempIndex); // [!] sollte nicht estimateDistance sondern calculateDistance() heißen! if (distance != 0) { // hier wird Voxel in entsprechenden Stack und ins OutputImage geschrieben this->InsertIndexTypeIntoQueueMap((distance), tempIndex); this->m_OutputImage->SetPixel(tempIndex, (m_InitializeValue - distance)); } } } } } // end left/right neighbor loop } // end check all neighbors // Now that all the potential neighbors have been // inserted we can get rid of the pixel in the front currentQueue->pop(); m_VoxelCounter++; if (currentQueue->empty()) { // if currently used queue is empty this->IncrementRegionGrowingState(); } } if (m_RegionGrowingState >= (m_InitializeValue) || m_DetectionStop) { this->m_IsAtEnd = true; // std::cout << "RegionGrowing finished !" << std::endl; // std::cout << "Detected point of leakage: " << m_DetectedLeakagePoint << std::endl; } } } // end namespace itk #endif diff --git a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h index 985cff7fad..d6f2757e67 100644 --- a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h +++ b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.h @@ -1,281 +1,281 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef __itkContourExtractor2DImageFilter_h #define __itkContourExtractor2DImageFilter_h #include "itkConceptChecking.h" #include "itkImageToPathFilter.h" #include "itkNumericTraits.h" #include "itkPolyLineParametricPath.h" -#include "vcl_deque.h" -#include "vcl_list.h" -#include +#include +#include +#include namespace itk { /** \class ContourExtractor2DImageFilter * \brief Computes a list of PolyLineParametricPath objects from the contours in * a 2D image. * * Uses the "marching squares" method to compute a the iso-valued contours of * the input 2D image for a given intensity value. Multiple outputs may be * produced because an image can have multiple contours at a given level, so it * is advised to call GetNumberOfOutputs() and GetOutput(n) to retrieve all of * the contours. The contour value to be extracted can be set with * SetContourValue(). Image intensities will be linearly interpolated to provide * sub-pixel resolution for the output contours. * * The marching squares algorithm is a special case of the marching cubes * algorithm (Lorensen, William and Harvey E. Cline. Marching Cubes: A High * Resolution 3D Surface Construction Algorithm. Computer Graphics (SIGGRAPH 87 * Proceedings) 21(4) July 1987, p. 163-170). A simple explanation is available * here: http://www.essi.fr/~lingrand/MarchingCubes/algo.html * * There is a single ambiguous case in the marching squares algorithm: if a * given 2x2-pixel square has two high-valued and two low-valued pixels, each * pair diagonally adjacent. (Where high- and low-valued is with respect to the * contour value sought.) In this case, either the high-valued pixels can be * connected into the same "object" (where groups of pixels encircled by a given * contour are considered an object), or the low-valued pixels can be connected. * This is the "face connected" versus "face + vertex connected" (or 4- versus * 4-connected) distinction: high-valued pixels most be treated as one, and * low-valued as the other. By default, high-valued pixels are treated as * "face-connected" and low-valued pixels are treated as "face + vertex" * connected. To reverse this, call VertexConnectHighPixelsOn(); * * Outputs are not guaranteed to be closed paths: contours which intersect the * image edge will be left open. All other paths will be closed. (The * closed-ness of a path can be tested by checking whether the beginning point * is the same as the end point.) * * Produced paths are oriented. Following the path from beginning to end, image * intensity values lower than the contour value are to the left of the path and * intensity values grater than the contour value are to the right. In other * words, the image gradient at a path segment is (approximately) in the direct * of that segment rotated right by 90 degrees, because the image intensity * values increase from left-to-right across the segment. This means that the * generated contours will circle clockwise around "hills" of * above-contour-value intensity, and counter-clockwise around "depressions" of * below-contour-value intensity. This convention can be reversed by calling * ReverseContourOrientationOn(). * * By default the input image's largest possible region will be processed; call * SetRequestedRegion() to process a different region, or ClearRequestedRegion() * to revert to the default value. Note that the requested regions are usually * set on the output; however since paths have no notion of a "region", this * must be set at the filter level. * * This class was contributed to the Insight Journal by Zachary Pincus. * http://insight-journal.org/midas/handle.php?handle=1926/165 * * \sa Image * \sa Path * \sa PolyLineParametricPath * */ template class ITK_EXPORT ContourExtractor2DImageFilter : public ImageToPathFilter> { public: /** Extract dimension from input and output image. */ itkStaticConstMacro(InputImageDimension, unsigned int, TInputImage::ImageDimension); /** Convenient typedefs for simplifying declarations. */ typedef TInputImage InputImageType; typedef PolyLineParametricPath<2> OutputPathType; /** Standard class typedefs. */ typedef ContourExtractor2DImageFilter Self; typedef ImageToPathFilter Superclass; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** Run-time type information (and related methods). */ itkTypeMacro(ContourExtractor2DImageFilter, ImageToPathFilter); /** Image and path typedef support. */ typedef typename InputImageType::Pointer InputImagePointer; typedef typename InputImageType::PixelType InputPixelType; typedef typename InputImageType::IndexType InputIndexType; typedef typename InputImageType::OffsetType InputOffsetType; typedef typename InputImageType::RegionType InputRegionType; typedef typename OutputPathType::Pointer OutputPathPointer; typedef typename OutputPathType::VertexType VertexType; typedef typename OutputPathType::VertexListType VertexListType; /** Real type associated to the input pixel type. */ typedef typename NumericTraits::RealType InputRealType; typedef typename VertexListType::ConstPointer VertexListConstPointer; /** Control the orientation of the contours with reference to the image * gradient. (See class documentation.) */ itkSetMacro(ReverseContourOrientation, bool); itkGetConstReferenceMacro(ReverseContourOrientation, bool); itkBooleanMacro(ReverseContourOrientation); /** Control whether high- or low-valued pixels are vertex-connected. * Default is for low-valued pixels to be vertex-connected. * (See class documentation.) */ itkSetMacro(VertexConnectHighPixels, bool); itkGetConstReferenceMacro(VertexConnectHighPixels, bool); itkBooleanMacro(VertexConnectHighPixels); /** Control whether the largest possible input region is used, or if a * custom requested region is to be used. */ void SetRequestedRegion(const InputRegionType region); itkGetConstReferenceMacro(RequestedRegion, InputRegionType); void ClearRequestedRegion(); /** Set/Get the image intensity value that the contours should follow. * This is the equivalent of an iso-value in Marching Squares. */ itkSetMacro(ContourValue, InputRealType); itkGetConstReferenceMacro(ContourValue, InputRealType); #ifdef ITK_USE_CONCEPT_CHECKING /** Begin concept checking */ itkConceptMacro(DimensionShouldBe2, (Concept::SameDimension)); itkConceptMacro(InputPixelTypeComparable, (Concept::Comparable)); itkConceptMacro(InputHasPixelTraitsCheck, (Concept::HasPixelTraits)); itkConceptMacro(InputHasNumericTraitsCheck, (Concept::HasNumericTraits)); /** End concept checking */ #endif protected: ContourExtractor2DImageFilter(); ~ContourExtractor2DImageFilter() override; void PrintSelf(std::ostream &os, Indent indent) const override; void GenerateData() override; /** ContourExtractor2DImageFilter manually controls the input requested * region via SetRequestedRegion and ClearRequestedRegion, so it must * override the superclass method. */ void GenerateInputRequestedRegion() override; private: VertexType InterpolateContourPosition(InputPixelType fromValue, InputPixelType toValue, InputIndexType fromIndex, InputOffsetType toOffset); void AddSegment(const VertexType from, const VertexType to); void FillOutputs(); ContourExtractor2DImageFilter(const Self &); // purposely not implemented void operator=(const Self &); // purposely not implemented InputRealType m_ContourValue; bool m_ReverseContourOrientation; bool m_VertexConnectHighPixels; bool m_UseCustomRegion; InputRegionType m_RequestedRegion; unsigned int m_NumberOfContoursCreated; // Represent each contour as deque of vertices to facilitate addition of // nodes at beginning or end. At the end of the processing, we will copy // the contour into a PolyLineParametricPath. // We subclass the deque to store an additional bit of information: an // identification number for each growing contour. We use this number so // that when it becomes necessary to merge two growing contours, we can // merge the newer one into the older one. This helps because then we can // guarantee that the output contour list is ordered from left to right, // top to bottom (in terms of the first pixel of the contour encountered // by the marching squares). Currently we make no guarantees that this // pixel is the first pixel in the contour list, just that the contours // are so ordered in the output. Ensuring this latter condition (first // pixel traversed = first pixel in contour) would be possible by either // changing the merging rules, which would make the contouring operation // slower, or by storing additional data as to which pixel was first. - class ContourType : public vcl_deque + class ContourType : public std::deque { public: unsigned int m_ContourNumber; }; // Store all the growing contours in a list. We may need to delete contours // from anywhere in the sequence (when we merge them together), so we need to // use a list instead of a vector or similar. - typedef vcl_list ContourContainer; + typedef std::list ContourContainer; typedef typename ContourContainer::iterator ContourRef; // declare the hash function we are using for the hash_map. struct VertexHash { typedef typename VertexType::CoordRepType CoordinateType; inline size_t operator()(const VertexType &k) const { // Xor the hashes of the vertices together, after multiplying the // first by some number, so that identical (x,y) vertex indices // don't all hash to the same bucket. This is a decent if not // optimal hash. const size_t hashVertex1 = this->float_hash(k[0] * 0xbeef); const size_t hashVertex2 = this->float_hash(k[1]); const size_t hashValue = hashVertex1 ^ hashVertex2; return hashValue; } // Define hash function for floats. Based on method from // http://www.brpreiss.com/books/opus4/html/page217.html inline size_t float_hash(const CoordinateType &k) const { if (k == 0) { return 0; } int exponent; - CoordinateType mantissa = vcl_frexp(k, &exponent); - size_t value = static_cast(vcl_fabs(mantissa)); + CoordinateType mantissa = std::frexp(k, &exponent); + size_t value = static_cast(std::fabs(mantissa)); value = (2 * value - 1) * ~0U; return value; } }; // We use a hash to associate the endpoints of each contour with the // contour itself. This makes it easy to look up which contour we should add // a new arc to. // We can't store the contours themselves in the hashtable because we // need to have two tables (one to hash from beginpoint -> contour and one // for endpoint -> contour), and sometimes will remove a contour from the // tables (if it has been closed or merged with another contour). So in the // hash table we store a reference to the contour. Because sometimes we will // need to merge contours, we need to be able to quickly remove contours // from our list when they have been merged into another. Thus, we store // an iterator pointing to the contour in the list. - typedef itksys::hash_map VertexToContourMap; + typedef std::unordered_map VertexToContourMap; typedef typename VertexToContourMap::iterator VertexMapIterator; typedef typename VertexToContourMap::value_type VertexContourRefPair; // The contours we find in the image are stored here ContourContainer m_Contours; // And indexed by their beginning and ending points here VertexToContourMap m_ContourStarts; VertexToContourMap m_ContourEnds; }; } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "itkContourExtractor2DImageFilter.txx" #endif #endif diff --git a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx index 77bc5daa67..f103d54896 100644 --- a/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx +++ b/Modules/Segmentation/Algorithms/itkContourExtractor2DImageFilter.txx @@ -1,536 +1,536 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef __itkContourExtractor2DImageFilter_txx #define __itkContourExtractor2DImageFilter_txx #include "itkConstShapedNeighborhoodIterator.h" #include "itkConstShapedNeighborhoodIterator.h" #include "itkContourExtractor2DImageFilter.h" #include "itkProgressReporter.h" -#include "vcl_cmath.h" +#include namespace itk { // Constructor template ContourExtractor2DImageFilter::ContourExtractor2DImageFilter() { this->m_ContourValue = NumericTraits::Zero; this->m_ReverseContourOrientation = false; this->m_VertexConnectHighPixels = false; this->m_UseCustomRegion = false; this->m_NumberOfContoursCreated = 0; } // Destructor template ContourExtractor2DImageFilter::~ContourExtractor2DImageFilter() { } template void ContourExtractor2DImageFilter::GenerateData() { // Make sure the structures for containing, looking up, and numbering the // growing contours are empty and ready. m_Contours.clear(); m_ContourStarts.clear(); m_ContourEnds.clear(); m_NumberOfContoursCreated = 0; // Set up an iterator to "march the squares" across the image. // We associate each 2px-by-2px square with the pixel in the upper left of // that square. We then iterate across the image, examining these 2x2 squares // and building the contour. By iterating the upper-left pixel of our // "current square" across every pixel in the image except those on the // bottom row and rightmost column, we have visited every valid square in the // image. InputRegionType region = this->GetInput()->GetRequestedRegion(); typename InputRegionType::SizeType shrunkSize = region.GetSize(); shrunkSize[0] -= 1; shrunkSize[1] -= 1; InputRegionType shrunkRegion(region.GetIndex(), shrunkSize); // Set up a progress reporter ProgressReporter progress(this, 0, shrunkRegion.GetNumberOfPixels()); // A 1-pixel radius sets up a neighborhood with the following indices: // 0 1 2 // 3 4 5 // 6 7 8 // We are interested only in the square of 4,5,7,8 which is the 2x2 square // with the center pixel at the top-left. So we only activate the // coresponding offsets, and only query pixels 4, 5, 7, and 8 with the // iterator's GetPixel method. typedef ConstShapedNeighborhoodIterator SquareIterator; typename SquareIterator::RadiusType radius = {{1, 1}}; SquareIterator it(radius, this->GetInput(), shrunkRegion); InputOffsetType none = {{0, 0}}; InputOffsetType right = {{1, 0}}; InputOffsetType down = {{0, 1}}; InputOffsetType diag = {{1, 1}}; it.ActivateOffset(none); it.ActivateOffset(right); it.ActivateOffset(down); it.ActivateOffset(diag); for (it.GoToBegin(); !it.IsAtEnd(); ++it) { // There are sixteen different possible square types, diagramed below. // A + indicates that the vertex is above the contour value, and a - // indicates that the vertex is below or equal to the contour value. // The vertices of each square are here numbered: // 01 // 23 // and treated as a binary value with the bits in that order. Thus each // square can be so numbered: // 0-- 1+- 2-+ 3++ 4-- 5+- 6-+ 7++ // -- -- -- -- +- +- +- +- // // 8-- 9+- 10-+ 11++ 12-- 13+- 14-+ 15++ // -+ -+ -+ -+ ++ ++ ++ ++ // // The position of the line segment that cuts through (or doesn't, in case // 0 and 15) each square is clear, except in cases 6 and 9. In this case, // where the segments are placed is determined by // m_VertexConnectHighPixels. If m_VertexConnectHighPixels is false, then // lines like are drawn through square 6, and lines like are drawn through // square 9. Otherwise, the situation is reversed. // Finally, recall that we draw the lines so that (moving from tail to // head) the lower-valued pixels are on the left of the line. So, for // example, case 1 entails a line slanting from the middle of the top of // the square to the middle of the left side of the square. // (1) Determine what number square we are currently inspecting. Remember // that as far as the neighborhood iterator is concerned, our square // 01 is numbered as 45 // 23 78 InputPixelType v0, v1, v2, v3; v0 = it.GetPixel(4); v1 = it.GetPixel(5); v2 = it.GetPixel(7); v3 = it.GetPixel(8); InputIndexType index = it.GetIndex(); unsigned char squareCase = 0; if (v0 > m_ContourValue) squareCase += 1; if (v1 > m_ContourValue) squareCase += 2; if (v2 > m_ContourValue) squareCase += 4; if (v3 > m_ContourValue) squareCase += 8; // Set up macros to find the ContinuousIndex where the contour intersects // one of the sides of the square. Normally macros should, of course, be // eschewed, but since this is an inner loop not calling the function four // times when two would do is probably worth while. Plus, copy-pasting // these into the switch below is even worse. InterpolateContourPosition // takes the values at two vertices, the index of the first vertex, and the // offset between the two vertices. #define TOP_ this->InterpolateContourPosition(v0, v1, index, right) #define BOTTOM_ this->InterpolateContourPosition(v2, v3, index + down, right) #define LEFT_ this->InterpolateContourPosition(v0, v2, index, down) #define RIGHT_ this->InterpolateContourPosition(v1, v3, index + right, down) // (2) Add line segments to the growing contours as defined by the cases. // AddSegment takes a "from" vertex and a "to" vertex, and adds it to the // a growing contour, creates a new contour, or merges two together. switch (squareCase) { case 0: // no line break; case 1: // top to left this->AddSegment(TOP_, LEFT_); break; case 2: // right to top this->AddSegment(RIGHT_, TOP_); break; case 3: // right to left this->AddSegment(RIGHT_, LEFT_); break; case 4: // left to bottom this->AddSegment(LEFT_, BOTTOM_); break; case 5: // top to bottom this->AddSegment(TOP_, BOTTOM_); break; case 6: if (m_VertexConnectHighPixels) { // left to top this->AddSegment(LEFT_, TOP_); // right to bottom this->AddSegment(RIGHT_, BOTTOM_); } else { // right to top this->AddSegment(RIGHT_, TOP_); // left to bottom this->AddSegment(LEFT_, BOTTOM_); } break; case 7: // right to bottom this->AddSegment(RIGHT_, BOTTOM_); break; case 8: // bottom to right this->AddSegment(BOTTOM_, RIGHT_); break; case 9: if (m_VertexConnectHighPixels) { // top to right this->AddSegment(TOP_, RIGHT_); // bottom to left this->AddSegment(BOTTOM_, LEFT_); } else { // top to left this->AddSegment(TOP_, LEFT_); // bottom to right this->AddSegment(BOTTOM_, RIGHT_); } break; case 10: // bottom to top this->AddSegment(BOTTOM_, TOP_); break; case 11: // bottom to left this->AddSegment(BOTTOM_, LEFT_); break; case 12: // left to right this->AddSegment(LEFT_, RIGHT_); break; case 13: // top to right this->AddSegment(TOP_, RIGHT_); break; case 14: // left to top this->AddSegment(LEFT_, TOP_); break; case 15: // no line break; } // switch squareCase progress.CompletedPixel(); } // iteration // Now create the outputs paths from the deques we've been using. this->FillOutputs(); m_Contours.clear(); m_ContourStarts.clear(); m_ContourEnds.clear(); m_NumberOfContoursCreated = 0; } template inline typename ContourExtractor2DImageFilter::VertexType ContourExtractor2DImageFilter::InterpolateContourPosition(InputPixelType fromValue, InputPixelType toValue, InputIndexType fromIndex, InputOffsetType toOffset) { VertexType output; // Now calculate the fraction of the way from 'from' to 'to' that the contour // crosses. Interpolate linearly: y = v0 + (v1 - v0) * x, and solve for the // x that gives y = m_ContourValue: x = (m_ContourValue - v0) / (v1 - v0). // This assumes that v0 and v1 are separated by exactly ONE unit. So the to // Offset. value must have exactly one component 1 and the other component 0. // Also this assumes that fromValue and toValue are different. Otherwise we // can't interpolate anything! itkAssertOrThrowMacro((fromValue != toValue), "source and destination are the same"); itkAssertOrThrowMacro(((toOffset[0] == 0 && toOffset[1] == 1) || (toOffset[0] == 1 && toOffset[1] == 0)), "toOffset has unexpected values"); double x = (m_ContourValue - static_cast(fromValue)) / (toValue - static_cast(fromValue)); output[0] = fromIndex[0] + x * toOffset[0]; output[1] = fromIndex[1] + x * toOffset[1]; return output; } template void ContourExtractor2DImageFilter::AddSegment(VertexType from, VertexType to) { if (from == to) { // Arc is degenerate: ignore, and the from/two point will be connected // later by other squares. Degeneracy happens when (and only when) a square // has exactly one vertex that is the contour value, and the rest are above // that value. return; } // Try to find an existing contour that starts where the new segment ends. VertexMapIterator newTail = m_ContourStarts.find(to); // Try to find an existing contour that ends where the new segment starts. VertexMapIterator newHead = m_ContourEnds.find(from); if (newTail != m_ContourStarts.end() && newHead != m_ContourEnds.end()) { // We need to connect these two contours. The act of connecting them will // add the needed arc. auto tail = newTail->second; itkAssertOrThrowMacro((tail->front() == to), "End doesn't match Beginning"); auto head = newHead->second; itkAssertOrThrowMacro((head->back() == from), "Beginning doesn't match End"); if (head == tail) { // We've closed a contour. Add the end point, and remove from the maps head->push_back(to); m_ContourStarts.erase(newTail); // erase the front of tail. Because head and tail are the same contour, // don't worry about erasing the front of head! m_ContourEnds.erase(newHead); // erase the end of head/tail. } else { // We have found two distinct contours that need to be joined. Careful // here: we want to keep the first segment in the list when merging so // that contours are always returned in top-to-bottom, right-to-left // order (with regard to the image pixel found to be inside the contour). if (tail->m_ContourNumber > head->m_ContourNumber) { // if tail was created later than head... // Copy tail to the end of head and remove // tail from everything. head->insert(head->end(), tail->begin(), tail->end()); // Now remove 'tail' from the list and the maps because it has been // subsumed. m_ContourStarts.erase(newTail); int erased = m_ContourEnds.erase(tail->back()); // There should be exactly one entry in the hash for that endpoint if (erased != 1) { itkWarningMacro(<< "There should be exactly one entry in the hash for that endpoint, but there are " << erased); } m_Contours.erase(tail); // remove from the master list // Now remove the old end of 'head' from the ends map and add // the new end. m_ContourEnds.erase(newHead); m_ContourEnds.insert(VertexContourRefPair(head->back(), head)); } else { // Copy head to the beginning of tail and remove // head from everything. tail->insert(tail->begin(), head->begin(), head->end()); // Now remove 'head' from the list and the maps because // it has been subsumed. m_ContourEnds.erase(newHead); int erased = m_ContourStarts.erase(head->front()); if (erased != 1) { itkWarningMacro(<< "There should be exactly one entry in the hash for that endpoint, but there are " << erased); } m_Contours.erase(head); // remove from the master list // Now remove the old start of 'tail' from the starts map and // add the new start. m_ContourStarts.erase(newTail); m_ContourStarts.insert(VertexContourRefPair(tail->front(), tail)); } } } else if (newTail == m_ContourStarts.end() && newHead == m_ContourEnds.end()) { // No contours found: add a new one. // Make it on the heap. It will be copied into m_Contours. ContourType contour; // Add the endpoints contour.push_front(from); contour.push_back(to); contour.m_ContourNumber = m_NumberOfContoursCreated++; // Add the contour to the end of the list and get a reference to it. m_Contours.push_back(contour); // recall that end() is an iterator to one past the back! auto newContour = --m_Contours.end(); // add the endpoints and an iterator pointing to the contour // in the list to the maps. m_ContourStarts.insert(VertexContourRefPair(from, newContour)); m_ContourEnds.insert(VertexContourRefPair(to, newContour)); } else if (newTail != m_ContourStarts.end() && newHead == m_ContourEnds.end()) { // Found a single contour to which the new arc should be prepended. auto tail = newTail->second; itkAssertOrThrowMacro((tail->front() == to), "End doesn't match Beginning"); tail->push_front(from); // erase the old start of this contour m_ContourStarts.erase(newTail); // Now add the new start of this contour. m_ContourStarts.insert(VertexContourRefPair(from, tail)); } else if (newTail == m_ContourStarts.end() && newHead != m_ContourEnds.end()) { // Found a single contour to which the new arc should be appended. auto head = newHead->second; itkAssertOrThrowMacro((head->back() == from), "Beginning doesn't match End"); head->push_back(to); // erase the old end of this contour m_ContourEnds.erase(newHead); // Now add the new start of this contour. m_ContourEnds.insert(VertexContourRefPair(to, head)); } } template void ContourExtractor2DImageFilter::FillOutputs() { this->SetNumberOfIndexedOutputs(m_Contours.size()); int i = 0; for (auto it = m_Contours.begin(); it != m_Contours.end(); it++, i++) { OutputPathPointer output = this->GetOutput(i); if (output.IsNull()) { // Static cast is OK because we know PathSource will make its templated // class type output = static_cast(this->MakeOutput(i).GetPointer()); this->SetNthOutput(i, output.GetPointer()); } typename VertexListType::Pointer path = const_cast(output->GetVertexList()); path->Initialize(); path->reserve(it->size()); // use std::vector version of 'reserve()' // instead of VectorContainer::Reserve() to work around // the fact that the latter is essentially std::vector::resize(), // which is not what we want. // Now put all the points from the contour deque into the path and // mark output as modified typedef typename ContourType::const_iterator ConstIteratorType; if (m_ReverseContourOrientation) { ConstIteratorType itC = (*it).end(); do { itC--; path->push_back(*itC); } while (itC != (*it).begin()); } else { ConstIteratorType itC = (*it).begin(); while (itC != (*it).end()) { path->push_back(*itC); itC++; } } output->Modified(); } } template void ContourExtractor2DImageFilter::SetRequestedRegion(const InputRegionType region) { itkDebugMacro("setting RequestedRegion to " << region); m_UseCustomRegion = true; if (this->m_RequestedRegion != region) { this->m_RequestedRegion = region; this->Modified(); } } template void ContourExtractor2DImageFilter::ClearRequestedRegion() { itkDebugMacro("Clearing RequestedRegion."); if (this->m_UseCustomRegion == true) { this->m_UseCustomRegion = false; this->Modified(); } } template void ContourExtractor2DImageFilter::GenerateInputRequestedRegion() { InputImageType *input = const_cast(this->GetInput()); if (!input) return; if (m_UseCustomRegion) { InputRegionType requestedRegion = m_RequestedRegion; if (requestedRegion.Crop(input->GetLargestPossibleRegion())) { input->SetRequestedRegion(requestedRegion); return; } else { // Couldn't crop the region (requested region is outside the largest // possible region). Throw an exception. // store what we tried to request (prior to trying to crop) input->SetRequestedRegion(requestedRegion); // build an exception InvalidRequestedRegionError e(__FILE__, __LINE__); e.SetLocation(ITK_LOCATION); e.SetDescription("Requested region is outside the largest possible region."); e.SetDataObject(input); throw e; } } else { input->SetRequestedRegion(input->GetLargestPossibleRegion()); } } /** * Standard "PrintSelf" method */ template void ContourExtractor2DImageFilter::PrintSelf(std::ostream &os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "ReverseContourOrientation: " << m_ReverseContourOrientation << std::endl; os << indent << "VertexConnectHighPixels: " << m_VertexConnectHighPixels << std::endl; os << indent << "UseCustomRegion: " << m_UseCustomRegion << std::endl; os << indent << "NumericTraits: " << m_UseCustomRegion << std::endl; os << indent << "NumberOfContoursCreated: " << m_NumberOfContoursCreated << std::endl; if (m_UseCustomRegion) { os << indent << "Custom region: " << m_RequestedRegion << std::endl; } typedef typename NumericTraits::PrintType InputRealPrintType; os << indent << "Contour value: " << static_cast(m_ContourValue) << std::endl; } } // end namespace itk #endif diff --git a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp index 27a8beb883..86e8a0b011 100644 --- a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp +++ b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp @@ -1,486 +1,486 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkCorrectorAlgorithm.h" #include "mitkContourUtils.h" #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageDataItem.h" #include #include "itkCastImageFilter.h" #include "itkImageDuplicator.h" #include "itkImageRegionIterator.h" mitk::CorrectorAlgorithm::CorrectorAlgorithm() : ImageToImageFilter(), m_FillColor(1), m_EraseColor(0) { } mitk::CorrectorAlgorithm::~CorrectorAlgorithm() { } template void ConvertBackToCorrectPixelType( itk::Image *, mitk::Image::Pointer target, itk::Image::Pointer segmentationPixelTypeImage) { typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::CastImageFilter CastImageFilterType; typename CastImageFilterType::Pointer castImageFilter = CastImageFilterType::New(); castImageFilter->SetInput(segmentationPixelTypeImage); castImageFilter->Update(); typename OutputImageType::Pointer tempItkImage = castImageFilter->GetOutput(); tempItkImage->DisconnectPipeline(); mitk::CastToMitkImage(tempItkImage, target); } void mitk::CorrectorAlgorithm::GenerateData() { Image::Pointer inputImage = ImageToImageFilter::GetInput(0); if (inputImage.IsNull() || inputImage->GetDimension() != 2) { itkExceptionMacro("CorrectorAlgorithm needs a 2D image as input."); } if (m_Contour.IsNull()) { itkExceptionMacro("CorrectorAlgorithm needs a Contour object as input."); } // copy the input (since m_WorkingImage will be changed later) m_WorkingImage = inputImage; TimeGeometry::Pointer originalGeometry = nullptr; if (inputImage->GetTimeGeometry()) { originalGeometry = inputImage->GetTimeGeometry()->Clone(); m_WorkingImage->SetTimeGeometry(originalGeometry); } else { itkExceptionMacro("Original image does not have a 'Time sliced geometry'! Cannot copy."); } Image::Pointer temporarySlice; // Convert to DefaultSegmentationDataType (because TobiasHeimannCorrectionAlgorithm relys on that data type) { itk::Image::Pointer correctPixelTypeImage; CastToItkImage(m_WorkingImage, correctPixelTypeImage); assert(correctPixelTypeImage.IsNotNull()); // possible bug in CastToItkImage ? // direction maxtrix is wrong/broken/not working after CastToItkImage, leading to a failed assertion in // mitk/Core/DataStructures/mitkSlicedGeometry3D.cpp, 479: // virtual void mitk::SlicedGeometry3D::SetSpacing(const mitk::Vector3D&): Assertion `aSpacing[0]>0 && aSpacing[1]>0 // && aSpacing[2]>0' failed // solution here: we overwrite it with an unity matrix itk::Image::DirectionType imageDirection; imageDirection.SetIdentity(); // correctPixelTypeImage->SetDirection(imageDirection); temporarySlice = this->GetOutput(); // temporarySlice = ImportItkImage( correctPixelTypeImage ); // m_FillColor = 1; m_EraseColor = 0; ImprovedHeimannCorrectionAlgorithm(correctPixelTypeImage); // this is suboptimal, needs to be kept synchronous to DefaultSegmentationDataType - if (inputImage->GetChannelDescriptor().GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT) + if (inputImage->GetChannelDescriptor().GetPixelType().GetComponentType() == itk::IOComponentEnum::USHORT) { // the cast at the beginning did not copy the data CastToMitkImage(correctPixelTypeImage, temporarySlice); } else { // it did copy the data and cast the pixel type AccessByItk_n(m_WorkingImage, ConvertBackToCorrectPixelType, (temporarySlice, correctPixelTypeImage)); } } temporarySlice->SetTimeGeometry(originalGeometry); } template itk::Index<2> mitk::CorrectorAlgorithm::ensureIndexInImage(ScalarType i0, ScalarType i1) { itk::Index<2> toReturn; itk::Size<5> size = m_WorkingImage->GetLargestPossibleRegion().GetSize(); toReturn[0] = std::min((ScalarType)(size[0] - 1), std::max((ScalarType)0.0, i0)); toReturn[1] = std::min((ScalarType)(size[1] - 1), std::max((ScalarType)0.0, i1)); return toReturn; } bool mitk::CorrectorAlgorithm::ImprovedHeimannCorrectionAlgorithm( itk::Image::Pointer pic) { /*! Some documentation (not by the original author) TobiasHeimannCorrectionAlgorithm will be called, when the user has finished drawing a freehand line. There should be different results, depending on the line's properties: 1. Without any prior segmentation, the start point and the end point of the drawn line will be connected to a contour and the area enclosed by the contour will be marked as segmentation. 2. When the whole line is inside a segmentation, start and end point will be connected to a contour and the area of this contour will be subtracted from the segmentation. 3. When the line starts inside a segmentation and ends outside with only a single transition from segmentation to no-segmentation, nothing will happen. 4. When there are multiple transitions between inside-segmentation and outside-segmentation, the line will be divided in so called segments. Each segment is either fully inside or fully outside a segmentation. When it is inside a segmentation, its enclosed area will be subtracted from the segmentation. When the segment is outside a segmentation, its enclosed area it will be added to the segmentation. The algorithm is described in full length in Tobias Heimann's diploma thesis (MBI Technical Report 145, p. 37 - 40). */ ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice(m_WorkingImage, m_Contour); if (projectedContour.IsNull() || projectedContour->GetNumberOfVertices() < 2) return false; // Read the first point of the contour auto contourIter = projectedContour->Begin(); if (contourIter == projectedContour->End()) return false; itk::Index<2> previousIndex; previousIndex = ensureIndexInImage((*contourIter)->Coordinates[0], (*contourIter)->Coordinates[1]); ++contourIter; int currentColor = (pic->GetPixel(previousIndex) == m_FillColor); TSegData currentSegment; int countOfSegments = 1; bool firstSegment = true; auto contourEnd = projectedContour->End(); for (; contourIter != contourEnd; ++contourIter) { // Get current point itk::Index<2> currentIndex; currentIndex = ensureIndexInImage((*contourIter)->Coordinates[0] + 0.5, (*contourIter)->Coordinates[1] + 0.5); // Calculate length and slope double slopeX = currentIndex[0] - previousIndex[0]; double slopeY = currentIndex[1] - previousIndex[1]; double length = std::sqrt(slopeX * slopeX + slopeY * slopeY); double deltaX = slopeX / length; double deltaY = slopeY / length; for (double i = 0; i <= length && length > 0; i += 1) { itk::Index<2> temporaryIndex; temporaryIndex = ensureIndexInImage(previousIndex[0] + deltaX * i, previousIndex[1] + deltaY * i); if (!pic->GetLargestPossibleRegion().IsInside(temporaryIndex)) continue; if ((pic->GetPixel(temporaryIndex) == m_FillColor) != currentColor) { currentSegment.points.push_back(temporaryIndex); if (!firstSegment) { ModifySegment(currentSegment, pic); } else { firstSegment = false; } currentSegment = TSegData(); ++countOfSegments; currentColor = (pic->GetPixel(temporaryIndex) == m_FillColor); } currentSegment.points.push_back(temporaryIndex); } previousIndex = currentIndex; } return true; } void mitk::CorrectorAlgorithm::ColorSegment( const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image::Pointer pic) { int colorMode = (pic->GetPixel(segment.points[0]) == m_FillColor); int color = 0; if (colorMode) color = m_EraseColor; else color = m_FillColor; std::vector>::const_iterator indexIterator; std::vector>::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); for (; indexIterator != indexEnd; ++indexIterator) { pic->SetPixel(*indexIterator, color); } } itk::Image::Pointer mitk::CorrectorAlgorithm::CloneImage( itk::Image::Pointer pic) { typedef itk::Image ItkImageType; typedef itk::ImageDuplicator DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(pic); duplicator->Update(); return duplicator->GetOutput(); } itk::Index<2> mitk::CorrectorAlgorithm::GetFirstPoint( const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image::Pointer pic) { int colorMode = (pic->GetPixel(segment.points[0]) == m_FillColor); std::vector>::const_iterator indexIterator; std::vector>::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); itk::Index<2> index; for (; indexIterator != indexEnd; ++indexIterator) { for (int xOffset = -1; xOffset < 2; ++xOffset) { for (int yOffset = -1; yOffset < 2; ++yOffset) { index = ensureIndexInImage((*indexIterator)[0] - xOffset, (*indexIterator)[1] - yOffset); if ((pic->GetPixel(index) == m_FillColor) != colorMode) { return index; } } } } mitkThrow() << "No Starting point is found next to the curve."; } std::vector> mitk::CorrectorAlgorithm::FindSeedPoints( const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image::Pointer pic) { typedef itk::Image::Pointer ItkImagePointerType; std::vector> seedPoints; try { itk::Index<2> firstPoint = GetFirstPoint(segment, pic); seedPoints.push_back(firstPoint); } catch (const mitk::Exception&) { return seedPoints; } if (segment.points.size() < 4) return seedPoints; std::vector>::const_iterator indexIterator; std::vector>::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); ItkImagePointerType listOfPoints = CloneImage(pic); listOfPoints->FillBuffer(0); listOfPoints->SetPixel(seedPoints[0], 1); for (; indexIterator != indexEnd; ++indexIterator) { listOfPoints->SetPixel(*indexIterator, 2); } indexIterator = segment.points.begin(); indexIterator++; indexIterator++; indexEnd--; indexEnd--; for (; indexIterator != indexEnd; ++indexIterator) { bool pointFound = true; while (pointFound) { pointFound = false; itk::Index<2> index; itk::Index<2> index2; for (int xOffset = -1; xOffset < 2; ++xOffset) { for (int yOffset = -1; yOffset < 2; ++yOffset) { index = ensureIndexInImage((*indexIterator)[0] - xOffset, (*indexIterator)[1] - yOffset); index2 = index; if (listOfPoints->GetPixel(index2) > 0) continue; index[0] = index[0] - 1; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } index[0] = index[0] + 2; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } index[0] = index[0] - 1; index[1] = index[1] - 1; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } index[1] = index[1] + 2; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } } } } } return seedPoints; } int mitk::CorrectorAlgorithm::FillRegion( const std::vector> &seedPoints, itk::Image::Pointer pic) { int numberOfPixel = 0; int mode = (pic->GetPixel(seedPoints[0]) == m_FillColor); int drawColor = m_FillColor; if (mode) { drawColor = m_EraseColor; } std::vector> workPoints; workPoints = seedPoints; // workPoints.push_back(seedPoints[0]); while (workPoints.size() > 0) { itk::Index<2> currentIndex = workPoints.back(); workPoints.pop_back(); if ((pic->GetPixel(currentIndex) == m_FillColor) == mode) ++numberOfPixel; pic->SetPixel(currentIndex, drawColor); currentIndex = ensureIndexInImage(currentIndex[0] - 1, currentIndex[1]); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); currentIndex = ensureIndexInImage(currentIndex[0] + 2, currentIndex[1]); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); currentIndex = ensureIndexInImage(currentIndex[0] - 1, currentIndex[1] - 1); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); currentIndex = ensureIndexInImage(currentIndex[0], currentIndex[1] + 2); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); } return numberOfPixel; } void mitk::CorrectorAlgorithm::OverwriteImage( itk::Image::Pointer source, itk::Image::Pointer target) { typedef itk::Image ItkImageType; typedef itk::ImageRegionIterator ImageIteratorType; ImageIteratorType sourceIter(source, source->GetLargestPossibleRegion()); ImageIteratorType targetIter(target, target->GetLargestPossibleRegion()); while (!sourceIter.IsAtEnd()) { targetIter.Set(sourceIter.Get()); ++sourceIter; ++targetIter; } } bool mitk::CorrectorAlgorithm::ModifySegment(const TSegData &segment, itk::Image::Pointer pic) { typedef itk::Image::Pointer ItkImagePointerType; ItkImagePointerType firstSideImage = CloneImage(pic); ColorSegment(segment, firstSideImage); ItkImagePointerType secondSideImage = CloneImage(firstSideImage); std::vector> seedPoints = FindSeedPoints(segment, firstSideImage); if (seedPoints.size() < 1) return false; int firstSidePixel = FillRegion(seedPoints, firstSideImage); std::vector> secondSeedPoints = FindSeedPoints(segment, firstSideImage); if (secondSeedPoints.size() < 1) return false; int secondSidePixel = FillRegion(secondSeedPoints, secondSideImage); if (firstSidePixel < secondSidePixel) { OverwriteImage(firstSideImage, pic); } else { OverwriteImage(secondSideImage, pic); } return true; } diff --git a/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp b/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp index 86eebc1701..0d009c3680 100644 --- a/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp +++ b/Modules/Segmentation/Algorithms/mitkDiffImageApplier.cpp @@ -1,359 +1,359 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkDiffImageApplier.h" #include "mitkApplyDiffImageOperation.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "mitkRenderingManager.h" #include "mitkSegmentationInterpolationController.h" #include #include #include mitk::DiffImageApplier::DiffImageApplier() : m_Image(nullptr), m_SliceDifferenceImage(nullptr), m_SliceIndex(0), m_SliceDimension(0), m_TimeStep(0), m_Dimension0(0), m_Dimension1(0), m_Factor(1.0) { } mitk::DiffImageApplier::~DiffImageApplier() { } void mitk::DiffImageApplier::ExecuteOperation(Operation *operation) { auto *imageOperation = dynamic_cast(operation); if (imageOperation // we actually have the kind of operation that we can handle && imageOperation->IsImageStillValid()) // AND the image is not yet deleted { m_Image = imageOperation->GetImage(); Image::Pointer image3D = m_Image; // will be changed later in case of 3D+t m_SliceDifferenceImage = imageOperation->GetDiffImage(); m_TimeStep = imageOperation->GetTimeStep(); m_Factor = imageOperation->GetFactor(); if (m_SliceDifferenceImage->GetDimension() == 2) { m_SliceIndex = imageOperation->GetSliceIndex(); m_SliceDimension = imageOperation->GetSliceDimension(); switch (m_SliceDimension) { default: case 2: m_Dimension0 = 0; m_Dimension1 = 1; break; case 1: m_Dimension0 = 0; m_Dimension1 = 2; break; case 0: m_Dimension0 = 1; m_Dimension1 = 2; break; } if (m_SliceDifferenceImage->GetDimension() != 2 || (m_Image->GetDimension() < 3 || m_Image->GetDimension() > 4) || m_SliceDifferenceImage->GetDimension(0) != m_Image->GetDimension(m_Dimension0) || m_SliceDifferenceImage->GetDimension(1) != m_Image->GetDimension(m_Dimension1) || m_SliceIndex >= m_Image->GetDimension(m_SliceDimension)) { itkExceptionMacro( "Slice and image dimensions differ or slice index is too large. Sorry, cannot work like this."); return; } if (m_Image->GetDimension() == 4) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(m_Image); timeSelector->SetTimeNr(m_TimeStep); timeSelector->UpdateLargestPossibleRegion(); image3D = timeSelector->GetOutput(); } // this will do a long long if/else to find out both pixel types AccessFixedDimensionByItk(image3D, ItkImageSwitch2DDiff, 3); if (m_Factor == 1 || m_Factor == -1) { if (m_Factor == -1) { // multiply diff pixels by factor and then send this diff slice AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 2); } // just send the diff to SegmentationInterpolationController SegmentationInterpolationController *interpolator = SegmentationInterpolationController::InterpolatorForImage(m_Image); if (interpolator) { interpolator->BlockModified(true); interpolator->SetChangedSlice(m_SliceDifferenceImage, m_SliceDimension, m_SliceIndex, m_TimeStep); } m_Image->Modified(); if (interpolator) { interpolator->BlockModified(false); } if (m_Factor == -1) // return to normal values { AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 2); } } else // no trivial case, too lazy to do something else { m_Image->Modified(); // check if interpolation is called. prefer to send diff directly } RenderingManager::GetInstance()->RequestUpdateAll(); } else if (m_SliceDifferenceImage->GetDimension() == 3) { // ... if (m_SliceDifferenceImage->GetDimension(0) != m_Image->GetDimension(0) || m_SliceDifferenceImage->GetDimension(1) != m_Image->GetDimension(1) || m_SliceDifferenceImage->GetDimension(2) != m_Image->GetDimension(2) || m_TimeStep >= m_Image->GetDimension(3)) { itkExceptionMacro("Diff image size differs from original image size. Sorry, cannot work like this."); return; } if (m_Image->GetDimension() == 4) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(m_Image); timeSelector->SetTimeNr(m_TimeStep); timeSelector->UpdateLargestPossibleRegion(); image3D = timeSelector->GetOutput(); } // this will do a long long if/else to find out both pixel types AccessFixedDimensionByItk(image3D, ItkImageSwitch3DDiff, 3); if (m_Factor == 1 || m_Factor == -1) { if (m_Factor == -1) { // multiply diff pixels by factor and then send this diff slice AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 3); } // just send the diff to SegmentationInterpolationController SegmentationInterpolationController *interpolator = SegmentationInterpolationController::InterpolatorForImage(m_Image); if (interpolator) { interpolator->BlockModified(true); interpolator->SetChangedVolume(m_SliceDifferenceImage, m_TimeStep); } m_Image->Modified(); if (interpolator) { interpolator->BlockModified(false); } if (m_Factor == -1) // return to normal values { AccessFixedDimensionByItk(m_SliceDifferenceImage, ItkInvertPixelValues, 3); } } else // no trivial case, too lazy to do something else { m_Image->Modified(); // check if interpolation is called. prefer to send diff directly } RenderingManager::GetInstance()->RequestUpdateAll(); } else { itkExceptionMacro("Diff image must be 2D or 3D. Sorry, cannot work like this."); return; } } m_Image = nullptr; m_SliceDifferenceImage = nullptr; } mitk::DiffImageApplier *mitk::DiffImageApplier::GetInstanceForUndo() { static DiffImageApplier::Pointer s_Instance = DiffImageApplier::New(); return s_Instance; } // basically copied from mitk/Core/Algorithms/mitkImageAccessByItk.h #define myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, pixeltype, dimension, itkimage2) \ if (typeId == MapPixelComponentType::value) \ \ { \ typedef itk::Image ImageType; \ typedef mitk::ImageToItk ImageToItkType; \ itk::SmartPointer imagetoitk = ImageToItkType::New(); \ const mitk::Image *constImage = mitkImage; \ mitk::Image *nonConstImage = const_cast(constImage); \ nonConstImage->Update(); \ imagetoitk->SetInput(nonConstImage); \ imagetoitk->Update(); \ itkImageTypeFunction(imagetoitk->GetOutput(), itkimage2); \ \ } #define myMITKDiffImageApplierFilterAccessAllTypesByItk(mitkImage, itkImageTypeFunction, dimension, itkimage2) \ \ { \ myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, double, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk( \ mitkImage, \ itkImageTypeFunction, \ float, \ dimension, \ itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, int, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, \ itkImageTypeFunction, \ unsigned int, \ dimension, \ itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, short, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned short, dimension, itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, \ itkImageTypeFunction, \ char, \ dimension, \ itkimage2) else myMITKDiffImageApplierFilterAccessByItk(mitkImage, \ itkImageTypeFunction, \ unsigned char, \ dimension, \ itkimage2) \ \ } template void mitk::DiffImageApplier::ItkImageSwitch2DDiff(itk::Image *itkImage) { - const int typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType(); + const auto typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType(); myMITKDiffImageApplierFilterAccessAllTypesByItk(m_SliceDifferenceImage, ItkImageProcessing2DDiff, 2, itkImage); } template void mitk::DiffImageApplier::ItkImageSwitch3DDiff(itk::Image *itkImage) { - const int typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType(); + const auto typeId = m_SliceDifferenceImage->GetPixelType().GetComponentType(); myMITKDiffImageApplierFilterAccessAllTypesByItk(m_SliceDifferenceImage, ItkImageProcessing3DDiff, 3, itkImage); } template void mitk::DiffImageApplier::ItkImageProcessing2DDiff(itk::Image *diffImage, itk::Image *outputImage) { typedef itk::Image DiffImageType; typedef itk::Image VolumeImageType; typedef itk::ImageSliceIteratorWithIndex OutputSliceIteratorType; typedef itk::ImageRegionConstIterator DiffSliceIteratorType; typename VolumeImageType::RegionType sliceInVolumeRegion; sliceInVolumeRegion = outputImage->GetLargestPossibleRegion(); sliceInVolumeRegion.SetSize(m_SliceDimension, 1); // just one slice sliceInVolumeRegion.SetIndex(m_SliceDimension, m_SliceIndex); // exactly this slice, please OutputSliceIteratorType outputIterator(outputImage, sliceInVolumeRegion); outputIterator.SetFirstDirection(m_Dimension0); outputIterator.SetSecondDirection(m_Dimension1); DiffSliceIteratorType diffIterator(diffImage, diffImage->GetLargestPossibleRegion()); // iterate over output slice (and over input slice simultaneously) outputIterator.GoToBegin(); diffIterator.GoToBegin(); while (!outputIterator.IsAtEnd()) { while (!outputIterator.IsAtEndOfSlice()) { while (!outputIterator.IsAtEndOfLine()) { TPixel2 newValue = outputIterator.Get() + (TPixel2)((double)diffIterator.Get() * m_Factor); outputIterator.Set(newValue); ++outputIterator; ++diffIterator; } outputIterator.NextLine(); } outputIterator.NextSlice(); } } template void mitk::DiffImageApplier::ItkImageProcessing3DDiff(itk::Image *diffImage, itk::Image *outputImage) { typedef itk::Image DiffImageType; typedef itk::Image VolumeImageType; typedef itk::ImageRegionIterator OutputSliceIteratorType; typedef itk::ImageRegionConstIterator DiffSliceIteratorType; OutputSliceIteratorType outputIterator(outputImage, outputImage->GetLargestPossibleRegion()); DiffSliceIteratorType diffIterator(diffImage, diffImage->GetLargestPossibleRegion()); // iterate over output slice (and over input slice simultaneously) outputIterator.GoToBegin(); diffIterator.GoToBegin(); while (!outputIterator.IsAtEnd()) { TPixel2 newValue = outputIterator.Get() + (TPixel2)((double)diffIterator.Get() * m_Factor); outputIterator.Set(newValue); ++outputIterator; ++diffIterator; } } #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable:4146) // unary minus operator applied to unsigned type, result still unsigned #endif template void mitk::DiffImageApplier::ItkInvertPixelValues(itk::Image *itkImage) { typedef itk::ImageRegionIterator> IteratorType; IteratorType iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { iter.Set(-(iter.Get())); ++iter; } } #ifdef _MSC_VER # pragma warning(pop) #endif diff --git a/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp index f9f57a08fb..5426cb0a21 100644 --- a/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp @@ -1,290 +1,290 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkSurfaceStampImageFilter.h" #include "mitkImageAccessByItk.h" #include "mitkImageWriteAccessor.h" #include "mitkTimeHelper.h" #include #include #include #include #include #include #include #include mitk::SurfaceStampImageFilter::SurfaceStampImageFilter() : m_MakeOutputBinary(false), m_OverwriteBackground(false), m_BackgroundValue(0.0), m_ForegroundValue(1.0) { } mitk::SurfaceStampImageFilter::~SurfaceStampImageFilter() { } void mitk::SurfaceStampImageFilter::GenerateInputRequestedRegion() { mitk::Image *outputImage = this->GetOutput(); if ((outputImage->IsInitialized() == false)) return; GenerateTimeInInputRegion(outputImage, this->GetInput()); } void mitk::SurfaceStampImageFilter::GenerateOutputInformation() { mitk::Image::ConstPointer inputImage = this->GetInput(); mitk::Image::Pointer outputImage = this->GetOutput(); itkDebugMacro(<< "GenerateOutputInformation()"); if (inputImage.IsNull() || (inputImage->IsInitialized() == false) || (inputImage->GetTimeGeometry() == nullptr)) return; if (m_MakeOutputBinary) outputImage->Initialize(mitk::MakeScalarPixelType(), *inputImage->GetTimeGeometry()); else outputImage->Initialize(inputImage->GetPixelType(), *inputImage->GetTimeGeometry()); outputImage->SetPropertyList(inputImage->GetPropertyList()->Clone()); } void mitk::SurfaceStampImageFilter::SetSurface(mitk::Surface *surface) { m_Surface = surface; } void mitk::SurfaceStampImageFilter::GenerateData() { mitk::Image::ConstPointer inputImage = this->GetInput(); if (inputImage.IsNull()) return; mitk::Image::Pointer outputImage = this->GetOutput(); if (outputImage->IsInitialized() == false) return; if (m_Surface.IsNull()) return; mitk::Image::RegionType outputRegion = outputImage->GetRequestedRegion(); int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); if (tmax > 0) { int t; for (t = tstart; t < tmax; ++t) { this->SurfaceStamp(t); } } else { this->SurfaceStamp(0); } } void mitk::SurfaceStampImageFilter::SurfaceStamp(int time) { mitk::Image::Pointer inputImage = this->GetInput(); const mitk::TimeGeometry *surfaceTimeGeometry = GetInput()->GetTimeGeometry(); const mitk::TimeGeometry *imageTimeGeometry = inputImage->GetTimeGeometry(); // Convert time step from image time-frame to surface time-frame mitk::TimePointType matchingTimePoint = imageTimeGeometry->TimeStepToTimePoint(time); mitk::TimeStepType surfaceTimeStep = surfaceTimeGeometry->TimePointToTimeStep(matchingTimePoint); vtkPolyData *polydata = m_Surface->GetVtkPolyData(surfaceTimeStep); if (!polydata) mitkThrow() << "Polydata is null."; vtkSmartPointer transformFilter = vtkSmartPointer::New(); transformFilter->SetInputData(polydata); // transformFilter->ReleaseDataFlagOn(); vtkSmartPointer transform = vtkSmartPointer::New(); BaseGeometry::Pointer geometry = surfaceTimeGeometry->GetGeometryForTimeStep(surfaceTimeStep); transform->PostMultiply(); transform->Concatenate(geometry->GetVtkTransform()->GetMatrix()); // take image geometry into account. vtk-Image information will be changed to unit spacing and zero origin below. BaseGeometry::Pointer imageGeometry = imageTimeGeometry->GetGeometryForTimeStep(time); transform->Concatenate(imageGeometry->GetVtkTransform()->GetLinearInverse()); transformFilter->SetTransform(transform); transformFilter->Update(); polydata = transformFilter->GetOutput(); if (!polydata || !polydata->GetNumberOfPoints()) mitkThrow() << "Polydata retrieved from transformation is null or has no points."; MeshType::Pointer mesh = MeshType::New(); - mesh->SetCellsAllocationMethod(MeshType::CellsAllocatedDynamicallyCellByCell); + mesh->SetCellsAllocationMethod(itk::MeshEnums::MeshClassCellsAllocationMethod::CellsAllocatedDynamicallyCellByCell); unsigned int numberOfPoints = polydata->GetNumberOfPoints(); mesh->GetPoints()->Reserve(numberOfPoints); vtkPoints *points = polydata->GetPoints(); MeshType::PointType point; for (unsigned int i = 0; i < numberOfPoints; i++) { double *aux = points->GetPoint(i); point[0] = aux[0]; point[1] = aux[1]; point[2] = aux[2]; mesh->SetPoint(i, point); } // Load the polygons into the itk::Mesh typedef MeshType::CellAutoPointer CellAutoPointerType; typedef MeshType::CellType CellType; typedef itk::TriangleCell TriangleCellType; typedef MeshType::PointIdentifier PointIdentifierType; typedef MeshType::CellIdentifier CellIdentifierType; // Read the number of polygons CellIdentifierType numberOfPolygons = 0; numberOfPolygons = polydata->GetNumberOfPolys(); PointIdentifierType numberOfCellPoints = 3; CellIdentifierType i = 0; for (i = 0; i < numberOfPolygons; i++) { vtkIdList *cellIds; vtkCell *vcell = polydata->GetCell(i); cellIds = vcell->GetPointIds(); CellAutoPointerType cell; auto *triangleCell = new TriangleCellType; PointIdentifierType k; for (k = 0; k < numberOfCellPoints; k++) { triangleCell->SetPointId(k, cellIds->GetId(k)); } cell.TakeOwnership(triangleCell); mesh->SetCell(i, cell); } if (!mesh->GetNumberOfPoints()) mitkThrow() << "Generated itk mesh is empty."; if (m_MakeOutputBinary) { this->SurfaceStampBinaryOutputProcessing(mesh); } else { AccessFixedDimensionByItk_1(inputImage, SurfaceStampProcessing, 3, mesh); } } void mitk::SurfaceStampImageFilter::SurfaceStampBinaryOutputProcessing(MeshType *mesh) { auto *inputImage = this->GetInput(); mitk::Image::Pointer outputImage = this->GetOutput(); typedef itk::Image BinaryImageType; BinaryImageType::Pointer itkInputImage; mitk::CastToItkImage(inputImage, itkInputImage); typedef itk::TriangleMeshToBinaryImageFilter FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput(mesh); filter->SetInfoImage(itkInputImage); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); BinaryImageType::Pointer resultImage = filter->GetOutput(); resultImage->DisconnectPipeline(); mitk::CastToMitkImage(resultImage, outputImage); } template void mitk::SurfaceStampImageFilter::SurfaceStampProcessing(itk::Image *input, MeshType *mesh) { typedef itk::Image ImageType; typedef itk::Image BinaryImageType; typedef itk::TriangleMeshToBinaryImageFilter FilterType; BinaryImageType::Pointer binaryInput = BinaryImageType::New(); binaryInput->SetSpacing(input->GetSpacing()); binaryInput->SetOrigin(input->GetOrigin()); binaryInput->SetDirection(input->GetDirection()); binaryInput->SetRegions(input->GetLargestPossibleRegion()); binaryInput->Allocate(); binaryInput->FillBuffer(0); FilterType::Pointer filter = FilterType::New(); filter->SetInput(mesh); filter->SetInfoImage(binaryInput); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); BinaryImageType::Pointer resultImage = filter->GetOutput(); resultImage->DisconnectPipeline(); mitk::Image::Pointer outputImage = this->GetOutput(); typename ImageType::Pointer itkOutputImage; mitk::CastToItkImage(outputImage, itkOutputImage); typedef itk::ImageRegionConstIterator BinaryIteratorType; typedef itk::ImageRegionConstIterator InputIteratorType; typedef itk::ImageRegionIterator OutputIteratorType; BinaryIteratorType sourceIter(resultImage, resultImage->GetLargestPossibleRegion()); sourceIter.GoToBegin(); InputIteratorType inputIter(input, input->GetLargestPossibleRegion()); inputIter.GoToBegin(); OutputIteratorType outputIter(itkOutputImage, itkOutputImage->GetLargestPossibleRegion()); outputIter.GoToBegin(); typename ImageType::PixelType inputValue; unsigned char sourceValue; auto fgValue = static_cast(m_ForegroundValue); auto bgValue = static_cast(m_BackgroundValue); while (!sourceIter.IsAtEnd()) { sourceValue = static_cast(sourceIter.Get()); inputValue = static_cast(inputIter.Get()); if (sourceValue != 0) outputIter.Set(fgValue); else if (m_OverwriteBackground) outputIter.Set(bgValue); else outputIter.Set(inputValue); ++sourceIter; ++inputIter; ++outputIter; } } diff --git a/Modules/Segmentation/CMakeLists.txt b/Modules/Segmentation/CMakeLists.txt index 9c77de4682..865c34266e 100644 --- a/Modules/Segmentation/CMakeLists.txt +++ b/Modules/Segmentation/CMakeLists.txt @@ -1,9 +1,9 @@ mitk_create_module( INCLUDE_DIRS Algorithms Controllers DataManagement Interactions Rendering SegmentationUtilities/BooleanOperations SegmentationUtilities/MorphologicalOperations DEPENDS MitkAlgorithmsExt MitkSurfaceInterpolation MitkGraphAlgorithms MitkContourModel MitkMultilabel PACKAGE_DEPENDS - PUBLIC ITK|QuadEdgeMesh + PUBLIC ITK|QuadEdgeMesh+RegionGrowing PRIVATE ITK|LabelMap+Watersheds VTK|ImagingGeneral ) add_subdirectory(Testing) diff --git a/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp b/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp index 6af64c8130..1e4d843a0b 100644 --- a/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp +++ b/Modules/Segmentation/DataManagement/mitkExtrudedContour.cpp @@ -1,383 +1,382 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkExtrudedContour.h" #include "mitkBaseProcess.h" #include "mitkNumericTypes.h" #include "mitkProportionalTimeGeometry.h" #include #include #include #include #include #include #include #include #include #include // vtkButterflySubdivisionFilter * subdivs; #include #include #include #include #include mitk::ExtrudedContour::ExtrudedContour() : m_Contour(nullptr), m_ClippingGeometry(nullptr), m_AutomaticVectorGeneration(false), m_Decimate(nullptr) { ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(1); SetTimeGeometry(timeGeometry); FillVector3D(m_Vector, 0.0, 0.0, 1.0); m_RightVector.Fill(0.0); m_ExtrusionFilter = vtkLinearExtrusionFilter::New(); m_ExtrusionFilter->CappingOff(); m_ExtrusionFilter->SetExtrusionTypeToVectorExtrusion(); double vtkvector[3] = {0, 0, 1}; // set extrusion vector m_ExtrusionFilter->SetVector(vtkvector); m_TriangleFilter = vtkTriangleFilter::New(); m_TriangleFilter->SetInputConnection(m_ExtrusionFilter->GetOutputPort()); m_SubdivisionFilter = vtkLinearSubdivisionFilter::New(); m_SubdivisionFilter->SetInputConnection(m_TriangleFilter->GetOutputPort()); m_SubdivisionFilter->SetNumberOfSubdivisions(4); m_ClippingBox = vtkPlanes::New(); m_ClipPolyDataFilter = vtkClipPolyData::New(); m_ClipPolyDataFilter->SetInputConnection(m_SubdivisionFilter->GetOutputPort()); m_ClipPolyDataFilter->SetClipFunction(m_ClippingBox); m_ClipPolyDataFilter->InsideOutOn(); m_Polygon = vtkPolygon::New(); m_ProjectionPlane = mitk::PlaneGeometry::New(); } mitk::ExtrudedContour::~ExtrudedContour() { m_ClipPolyDataFilter->Delete(); m_ClippingBox->Delete(); m_SubdivisionFilter->Delete(); m_TriangleFilter->Delete(); m_ExtrusionFilter->Delete(); m_Polygon->Delete(); } bool mitk::ExtrudedContour::IsInside(const Point3D &worldPoint) const { static double polygonNormal[3] = {0.0, 0.0, 1.0}; // project point onto plane float xt[3]; itk2vtk(worldPoint, xt); xt[0] = worldPoint[0] - m_Origin[0]; xt[1] = worldPoint[1] - m_Origin[1]; xt[2] = worldPoint[2] - m_Origin[2]; float dist = xt[0] * m_Normal[0] + xt[1] * m_Normal[1] + xt[2] * m_Normal[2]; xt[0] -= dist * m_Normal[0]; xt[1] -= dist * m_Normal[1]; xt[2] -= dist * m_Normal[2]; double x[3]; x[0] = xt[0] * m_Right[0] + xt[1] * m_Right[1] + xt[2] * m_Right[2]; x[1] = xt[0] * m_Down[0] + xt[1] * m_Down[1] + xt[2] * m_Down[2]; x[2] = 0; // determine whether it's in the selection loop and then evaluate point // in polygon only if absolutely necessary. if (x[0] >= this->m_ProjectedContourBounds[0] && x[0] <= this->m_ProjectedContourBounds[1] && x[1] >= this->m_ProjectedContourBounds[2] && x[1] <= this->m_ProjectedContourBounds[3] && this->m_Polygon->PointInPolygon(x, m_Polygon->Points->GetNumberOfPoints(), ((vtkDoubleArray *)this->m_Polygon->Points->GetData())->GetPointer(0), (double *)this->m_ProjectedContourBounds, polygonNormal) == 1) return true; else return false; } mitk::ScalarType mitk::ExtrudedContour::GetVolume() { return -1.0; } void mitk::ExtrudedContour::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } if (GetMTime() > m_LastCalculateExtrusionTime) { BuildGeometry(); BuildSurface(); } // if ( ( m_CalculateBoundingBox ) && ( m_PolyDataSeries.size() > 0 ) ) // CalculateBoundingBox(); } void mitk::ExtrudedContour::BuildSurface() { if (m_Contour.IsNull()) { SetVtkPolyData(nullptr); return; } // set extrusion contour vtkPolyData *polyData = vtkPolyData::New(); vtkCellArray *polys = vtkCellArray::New(); polys->InsertNextCell(m_Polygon->GetPointIds()); polyData->SetPoints(m_Polygon->GetPoints()); // float vtkpoint[3]; // unsigned int i, numPts = m_Polygon->GetNumberOfPoints(); // for(i=0; im_Polygon->Points->GetPoint(i); // pointids[i]=loopPoints->InsertNextPoint(vtkpoint); //} // polys->InsertNextCell( i, pointids ); // delete [] pointids; // polyData->SetPoints( loopPoints ); polyData->SetPolys(polys); polys->Delete(); m_ExtrusionFilter->SetInputData(polyData); polyData->Delete(); // set extrusion scale factor m_ExtrusionFilter->SetScaleFactor(GetGeometry()->GetExtentInMM(2)); SetVtkPolyData(m_SubdivisionFilter->GetOutput()); // if(m_ClippingGeometry.IsNull()) //{ // SetVtkPolyData(m_SubdivisionFilter->GetOutput()); //} // else //{ // m_ClipPolyDataFilter->SetInput(m_SubdivisionFilter->GetOutput()); // mitk::BoundingBox::BoundsArrayType bounds=m_ClippingGeometry->GetBounds(); // m_ClippingBox->SetBounds(bounds[0], bounds[1], bounds[2], bounds[3], bounds[4], bounds[5]); // m_ClippingBox->SetTransform(GetGeometry()->GetVtkTransform()); // m_ClipPolyDataFilter->SetClipFunction(m_ClippingBox); // m_ClipPolyDataFilter->SetValue(0); // SetVtkPolyData(m_ClipPolyDataFilter->GetOutput()); //} m_LastCalculateExtrusionTime.Modified(); } void mitk::ExtrudedContour::BuildGeometry() { if (m_Contour.IsNull()) return; // Initialize(1); Vector3D nullvector; nullvector.Fill(0.0); float xProj[3]; unsigned int i; unsigned int numPts = 20; // m_Contour->GetNumberOfPoints(); mitk::Contour::PathPointer path = m_Contour->GetContourPath(); mitk::Contour::PathType::InputType cstart = path->StartOfInput(); mitk::Contour::PathType::InputType cend = path->EndOfInput(); mitk::Contour::PathType::InputType cstep = (cend - cstart) / numPts; mitk::Contour::PathType::InputType ccur; // Part I: guarantee/calculate legal vectors m_Vector.Normalize(); itk2vtk(m_Vector, m_Normal); // check m_Vector if (mitk::Equal(m_Vector, nullvector) || m_AutomaticVectorGeneration) { if (m_AutomaticVectorGeneration == false) itkWarningMacro("Extrusion vector is 0 (" << m_Vector << "); trying to use normal of polygon"); vtkPoints *loopPoints = vtkPoints::New(); // mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin(); double vtkpoint[3]; unsigned int i = 0; for (i = 0, ccur = cstart; i < numPts; ++i, ccur += cstep) { itk2vtk(path->Evaluate(ccur), vtkpoint); loopPoints->InsertNextPoint(vtkpoint); } // Make sure points define a loop with a m_Normal vtkPolygon::ComputeNormal(loopPoints, m_Normal); loopPoints->Delete(); vtk2itk(m_Normal, m_Vector); if (mitk::Equal(m_Vector, nullvector)) { itkExceptionMacro("Cannot calculate normal of polygon"); } } // check m_RightVector if ((mitk::Equal(m_RightVector, nullvector)) || (mitk::Equal(m_RightVector * m_Vector, 0.0) == false)) { if (mitk::Equal(m_RightVector, nullvector)) { itkDebugMacro("Right vector is 0. Calculating."); } else { itkWarningMacro("Right vector (" << m_RightVector << ") not perpendicular to extrusion vector " << m_Vector << ": " << m_RightVector * m_Vector); } // calculate a legal m_RightVector if (mitk::Equal(m_Vector[1], 0.0f) == false) { FillVector3D(m_RightVector, 1.0f, -m_Vector[0] / m_Vector[1], 0.0f); m_RightVector.Normalize(); } else { FillVector3D(m_RightVector, 0.0f, 1.0f, 0.0f); } } // calculate down-vector VnlVector rightDV = m_RightVector.GetVnlVector(); rightDV.normalize(); vnl2vtk(rightDV, m_Right); VnlVector downDV = vnl_cross_3d(m_Vector.GetVnlVector(), rightDV); downDV.normalize(); vnl2vtk(downDV, m_Down); // Part II: calculate plane as base for extrusion, project the contour // on this plane and store as polygon for IsInside test and BoundingBox calculation // initialize m_ProjectionPlane, yet with origin at 0 m_ProjectionPlane->InitializeStandardPlane(rightDV, downDV); // create vtkPolygon from contour and simultaneously determine 2D bounds of // contour projected on m_ProjectionPlane // mitk::Contour::PointsContainerIterator pointsIt = m_Contour->GetPoints()->Begin(); m_Polygon->Points->Reset(); m_Polygon->Points->SetNumberOfPoints(numPts); m_Polygon->PointIds->Reset(); m_Polygon->PointIds->SetNumberOfIds(numPts); mitk::Point2D pt2d; mitk::Point3D pt3d; mitk::Point2D min, max; min.Fill(itk::NumericTraits::max()); max.Fill(itk::NumericTraits::min()); xProj[2] = 0.0; for (i = 0, ccur = cstart; i < numPts; ++i, ccur += cstep) { pt3d.CastFrom(path->Evaluate(ccur)); m_ProjectionPlane->Map(pt3d, pt2d); xProj[0] = pt2d[0]; if (pt2d[0] < min[0]) min[0] = pt2d[0]; if (pt2d[0] > max[0]) max[0] = pt2d[0]; xProj[1] = pt2d[1]; if (pt2d[1] < min[1]) min[1] = pt2d[1]; if (pt2d[1] > max[1]) max[1] = pt2d[1]; m_Polygon->Points->SetPoint(i, xProj); m_Polygon->PointIds->SetId(i, i); } // shift parametric origin to (0,0) for (i = 0; i < numPts; ++i) { double *pt = this->m_Polygon->Points->GetPoint(i); pt[0] -= min[0]; pt[1] -= min[1]; itkDebugMacro(<< i << ": (" << pt[0] << "," << pt[1] << "," << pt[2] << ")"); } this->m_Polygon->GetBounds(m_ProjectedContourBounds); // m_ProjectedContourBounds[4]=-1.0; m_ProjectedContourBounds[5]=1.0; // calculate origin (except translation along the normal) and bounds // of m_ProjectionPlane: // origin is composed of the minimum x-/y-coordinates of the polygon, // bounds from the extent of the polygon, both after projecting on the plane mitk::Point3D origin; m_ProjectionPlane->Map(min, origin); ScalarType bounds[6] = {0, max[0] - min[0], 0, max[1] - min[1], 0, 1}; m_ProjectionPlane->SetBounds(bounds); m_ProjectionPlane->SetOrigin(origin); // Part III: initialize geometry if (m_ClippingGeometry.IsNotNull()) { ScalarType min_dist = itk::NumericTraits::max(), max_dist = itk::NumericTraits::min(), dist; unsigned char i; for (i = 0; i < 8; ++i) { dist = m_ProjectionPlane->SignedDistance(m_ClippingGeometry->GetCornerPoint(i)); if (dist < min_dist) min_dist = dist; if (dist > max_dist) max_dist = dist; } // incorporate translation along the normal into origin origin = origin + m_Vector * min_dist; m_ProjectionPlane->SetOrigin(origin); bounds[5] = max_dist - min_dist; } else bounds[5] = 20; itk2vtk(origin, m_Origin); mitk::BaseGeometry::Pointer g3d = GetGeometry(0); assert(g3d.IsNotNull()); g3d->SetBounds(bounds); g3d->SetIndexToWorldTransform(m_ProjectionPlane->GetIndexToWorldTransform()); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(g3d, 1); SetTimeGeometry(timeGeometry); } -unsigned long mitk::ExtrudedContour::GetMTime() const +itk::ModifiedTimeType mitk::ExtrudedContour::GetMTime() const { - unsigned long latestTime = Superclass::GetMTime(); + auto latestTime = Superclass::GetMTime(); if (m_Contour.IsNotNull()) { - unsigned long localTime; - localTime = m_Contour->GetMTime(); + auto localTime = m_Contour->GetMTime(); if (localTime > latestTime) latestTime = localTime; } return latestTime; } diff --git a/Modules/Segmentation/DataManagement/mitkExtrudedContour.h b/Modules/Segmentation/DataManagement/mitkExtrudedContour.h index 0d50912071..913e6c3434 100644 --- a/Modules/Segmentation/DataManagement/mitkExtrudedContour.h +++ b/Modules/Segmentation/DataManagement/mitkExtrudedContour.h @@ -1,127 +1,127 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED #define MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED #include "mitkBoundingObject.h" #include #include #include #include #include class vtkLinearExtrusionFilter; class vtkPlanes; class vtkClipPolyData; class vtkLinearSubdivisionFilter; class vtkTriangleFilter; class vtkDecimatePro; class vtkPolygon; namespace mitk { //##Documentation //## @brief Data class containing a bounding-object created by //## extruding a Contour along a vector //## //## The m_Contour is extruded in the direction m_Vector until //## reaching m_ClippingGeometry. //## @ingroup Data /** * \deprecatedSince{2015_05} ExtrudedContour is deprecated. It will be removed in the next release. * Becomes obsolete. Refer to http://docs.mitk.org/nightly/InteractionMigration.html . */ class MITKSEGMENTATION_EXPORT ExtrudedContour : public BoundingObject { public: mitkClassMacro(ExtrudedContour, BoundingObject); itkFactorylessNewMacro(Self); itkCloneMacro(Self); mitk::ScalarType GetVolume() override; bool IsInside(const Point3D &p) const override; void UpdateOutputInformation() override; //##Documentation //## @brief Contour to extrude itkGetConstObjectMacro(Contour, mitk::Contour); itkSetObjectMacro(Contour, mitk::Contour); //##Documentation //## @brief Vector to specify the direction of the extrusion mitkGetVectorMacro(Vector, mitk::Vector3D); mitkSetVectorMacro(Vector, mitk::Vector3D); itkGetConstMacro(AutomaticVectorGeneration, bool); itkSetMacro(AutomaticVectorGeneration, bool); itkBooleanMacro(AutomaticVectorGeneration); //##Documentation //## @brief Optional vector to specify the orientation of the bounding-box mitkGetVectorMacro(RightVector, mitk::Vector3D); mitkSetVectorMacro(RightVector, mitk::Vector3D); //##Documentation //## @brief Optional geometry for clipping the extruded contour itkGetConstObjectMacro(ClippingGeometry, mitk::BaseGeometry); itkSetObjectMacro(ClippingGeometry, mitk::BaseGeometry); - unsigned long GetMTime() const override; + itk::ModifiedTimeType GetMTime() const override; protected: ExtrudedContour(); ~ExtrudedContour() override; void BuildSurface(); void BuildGeometry(); mitk::Contour::Pointer m_Contour; mitk::Vector3D m_Vector; mitk::Vector3D m_RightVector; mitk::BaseGeometry::Pointer m_ClippingGeometry; bool m_AutomaticVectorGeneration; vtkPolygon *m_Polygon; #if ((VTK_MAJOR_VERSION > 4) || ((VTK_MAJOR_VERSION == 4) && (VTK_MINOR_VERSION >= 4))) double m_ProjectedContourBounds[6]; #else float m_ProjectedContourBounds[6]; #endif mitk::PlaneGeometry::Pointer m_ProjectionPlane; //##Documentation //## @brief For fast projection on plane float m_Right[3]; float m_Down[3]; #if ((VTK_MAJOR_VERSION > 4) || ((VTK_MAJOR_VERSION == 4) && (VTK_MINOR_VERSION >= 4))) double m_Normal[3]; #else float m_Normal[3]; #endif float m_Origin[3]; vtkLinearExtrusionFilter *m_ExtrusionFilter; vtkTriangleFilter *m_TriangleFilter; vtkDecimatePro *m_Decimate; vtkLinearSubdivisionFilter *m_SubdivisionFilter; vtkPlanes *m_ClippingBox; vtkClipPolyData *m_ClipPolyDataFilter; itk::TimeStamp m_LastCalculateExtrusionTime; }; } #endif /* MITKEXTRUDEDCONTOUR_H_HEADER_INCLUDED */ diff --git a/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp index 1f5dc1032e..cbc5a650cb 100644 --- a/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp +++ b/Modules/Segmentation/Interactions/mitkBinaryThresholdBaseTool.cpp @@ -1,122 +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 "mitkBinaryThresholdBaseTool.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageStatisticsHolder.h" #include "mitkLabelSetImage.h" #include #include mitk::BinaryThresholdBaseTool::BinaryThresholdBaseTool() : m_SensibleMinimumThreshold(-100), m_SensibleMaximumThreshold(+100), m_LowerThreshold(1), m_UpperThreshold(1) { } mitk::BinaryThresholdBaseTool::~BinaryThresholdBaseTool() { } void mitk::BinaryThresholdBaseTool::SetThresholdValues(double lower, double upper) { /* If value is not in the min/max range, do nothing. In that case, this method will be called again with a proper value right after. The only known case where this happens is with an [0.0, 1.0[ image, where value could be an epsilon greater than the max. */ if (lower < m_SensibleMinimumThreshold || lower > m_SensibleMaximumThreshold || upper < m_SensibleMinimumThreshold || upper > m_SensibleMaximumThreshold) { return; } m_LowerThreshold = lower; m_UpperThreshold = upper; if (nullptr != this->GetPreviewSegmentation()) { UpdatePreview(); } } void mitk::BinaryThresholdBaseTool::InitiateToolByInput() { const auto referenceImage = this->GetReferenceData(); if (nullptr != referenceImage) { m_SensibleMinimumThreshold = std::numeric_limits::max(); m_SensibleMaximumThreshold = std::numeric_limits::lowest(); Image::StatisticsHolderPointer statistics = referenceImage->GetStatistics(); for (unsigned int ts = 0; ts < referenceImage->GetTimeSteps(); ++ts) { m_SensibleMinimumThreshold = std::min(m_SensibleMinimumThreshold, static_cast(statistics->GetScalarValueMin())); m_SensibleMaximumThreshold = std::max(m_SensibleMaximumThreshold, static_cast(statistics->GetScalarValueMax())); } if (m_LockedUpperThreshold) { m_LowerThreshold = (m_SensibleMaximumThreshold + m_SensibleMinimumThreshold) / 2.0; m_UpperThreshold = m_SensibleMaximumThreshold; } else { double range = m_SensibleMaximumThreshold - m_SensibleMinimumThreshold; m_LowerThreshold = m_SensibleMinimumThreshold + range / 3.0; m_UpperThreshold = m_SensibleMinimumThreshold + 2 * range / 3.0; } bool isFloatImage = false; - if ((referenceImage->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR) && - (referenceImage->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT || - referenceImage->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE)) + if ((referenceImage->GetPixelType().GetPixelType() == itk::IOPixelEnum::SCALAR) && + (referenceImage->GetPixelType().GetComponentType() == itk::IOComponentEnum::FLOAT || + referenceImage->GetPixelType().GetComponentType() == itk::IOComponentEnum::DOUBLE)) { isFloatImage = true; } IntervalBordersChanged.Send(m_SensibleMinimumThreshold, m_SensibleMaximumThreshold, isFloatImage); ThresholdingValuesChanged.Send(m_LowerThreshold, m_UpperThreshold); } } void mitk::BinaryThresholdBaseTool::DoUpdatePreview(const Image* inputAtTimeStep, const Image* /*oldSegAtTimeStep*/, Image* previewImage, TimeStepType timeStep) { if (nullptr != inputAtTimeStep && nullptr != previewImage) { AccessByItk_n(inputAtTimeStep, ITKThresholding, (previewImage, timeStep)); } } template void mitk::BinaryThresholdBaseTool::ITKThresholding(const itk::Image* inputImage, Image* segmentation, unsigned int timeStep) { typedef itk::Image ImageType; typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); filter->SetInput(inputImage); filter->SetLowerThreshold(m_LowerThreshold); filter->SetUpperThreshold(m_UpperThreshold); filter->SetInsideValue(this->GetUserDefinedActiveLabel()); filter->SetOutsideValue(0); filter->Update(); segmentation->SetVolume((void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); } diff --git a/Modules/Segmentation/Interactions/mitkTool.cpp b/Modules/Segmentation/Interactions/mitkTool.cpp index fb9721e97c..b5d0b702ed 100644 --- a/Modules/Segmentation/Interactions/mitkTool.cpp +++ b/Modules/Segmentation/Interactions/mitkTool.cpp @@ -1,348 +1,353 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkTool.h" #include #include "mitkDisplayInteractor.h" #include "mitkDisplayActionEventBroadcast.h" #include "mitkImageReadAccessor.h" #include "mitkImageWriteAccessor.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTableProperty.h" #include "mitkProperties.h" #include "mitkVtkResliceInterpolationProperty.h" #include #include // us #include #include // itk #include +namespace mitk +{ + itkEventMacroDefinition(ToolEvent, itk::ModifiedEvent); +} + mitk::Tool::Tool(const char *type, const us::Module *interactorModule) : m_EventConfig(""), m_ToolManager(nullptr), m_PredicateImages(NodePredicateDataType::New("Image")), // for reference images m_PredicateDim3(NodePredicateDimension::New(3, 1)), m_PredicateDim4(NodePredicateDimension::New(4, 1)), m_PredicateDimension(mitk::NodePredicateOr::New(m_PredicateDim3, m_PredicateDim4)), m_PredicateImage3D(NodePredicateAnd::New(m_PredicateImages, m_PredicateDimension)), m_PredicateBinary(NodePredicateProperty::New("binary", BoolProperty::New(true))), m_PredicateNotBinary(NodePredicateNot::New(m_PredicateBinary)), m_PredicateSegmentation(NodePredicateProperty::New("segmentation", BoolProperty::New(true))), m_PredicateNotSegmentation(NodePredicateNot::New(m_PredicateSegmentation)), m_PredicateHelper(NodePredicateProperty::New("helper object", BoolProperty::New(true))), m_PredicateNotHelper(NodePredicateNot::New(m_PredicateHelper)), m_PredicateImageColorful(NodePredicateAnd::New(m_PredicateNotBinary, m_PredicateNotSegmentation)), m_PredicateImageColorfulNotHelper(NodePredicateAnd::New(m_PredicateImageColorful, m_PredicateNotHelper)), m_PredicateReference(NodePredicateAnd::New(m_PredicateImage3D, m_PredicateImageColorfulNotHelper)), m_IsSegmentationPredicate( NodePredicateAnd::New(NodePredicateOr::New(m_PredicateBinary, m_PredicateSegmentation), m_PredicateNotHelper)), m_InteractorType(type), m_DisplayInteractorConfigs(), m_InteractorModule(interactorModule) { } mitk::Tool::~Tool() { } bool mitk::Tool::CanHandle(const BaseData* referenceData, const BaseData* /*workingData*/) const { if (referenceData == nullptr) return false; return true; } void mitk::Tool::InitializeStateMachine() { if (m_InteractorType.empty()) return; try { auto isThisModule = nullptr == m_InteractorModule; auto module = isThisModule ? us::GetModuleContext()->GetModule() : m_InteractorModule; LoadStateMachine(m_InteractorType + ".xml", module); SetEventConfig(isThisModule ? "SegmentationToolsConfig.xml" : m_InteractorType + "Config.xml", module); } catch (const std::exception &e) { MITK_ERROR << "Could not load statemachine pattern " << m_InteractorType << ".xml with exception: " << e.what(); } } void mitk::Tool::Notify(InteractionEvent *interactionEvent, bool isHandled) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if (!isHandled) { this->HandleEvent(interactionEvent, nullptr); } } void mitk::Tool::ConnectActionsAndFunctions() { } bool mitk::Tool::FilterEvents(InteractionEvent *, DataNode *) { return true; } const char *mitk::Tool::GetGroup() const { return "default"; } void mitk::Tool::SetToolManager(ToolManager *manager) { m_ToolManager = manager; } mitk::ToolManager* mitk::Tool::GetToolManager() const { return m_ToolManager; } mitk::DataStorage* mitk::Tool::GetDataStorage() const { if (nullptr != m_ToolManager) { return m_ToolManager->GetDataStorage(); } return nullptr; } void mitk::Tool::Activated() { // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts // with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction // will still be enabled m_DisplayInteractorConfigs.clear(); std::vector> listEventObserver = us::GetModuleContext()->GetServiceReferences(); for (auto it = listEventObserver.begin(); it != listEventObserver.end(); ++it) { auto displayInteractor = dynamic_cast(us::GetModuleContext()->GetService(*it)); if (displayInteractor != nullptr) { // remember the original configuration m_DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig())); // here the alternative configuration is loaded displayInteractor->AddEventConfig(m_EventConfig.c_str()); } auto displayActionEventBroadcast = dynamic_cast(us::GetModuleContext()->GetService(*it)); if (displayActionEventBroadcast != nullptr) { // remember the original configuration m_DisplayInteractorConfigs.insert(std::make_pair(*it, displayActionEventBroadcast->GetEventConfig())); // here the alternative configuration is loaded displayActionEventBroadcast->AddEventConfig(m_EventConfig.c_str()); } } } void mitk::Tool::Deactivated() { // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework for (auto it = m_DisplayInteractorConfigs.begin(); it != m_DisplayInteractorConfigs.end(); ++it) { if (it->first) { auto displayInteractor = static_cast(us::GetModuleContext()->GetService(it->first)); if (displayInteractor != nullptr) { // here the regular configuration is loaded again displayInteractor->SetEventConfig(it->second); } auto displayActionEventBroadcast = dynamic_cast(us::GetModuleContext()->GetService(it->first)); if (displayActionEventBroadcast != nullptr) { // here the regular configuration is loaded again displayActionEventBroadcast->SetEventConfig(it->second); } } } m_DisplayInteractorConfigs.clear(); } itk::Object::Pointer mitk::Tool::GetGUI(const std::string &toolkitPrefix, const std::string &toolkitPostfix) { itk::Object::Pointer object; std::string classname = this->GetNameOfClass(); std::string guiClassname = toolkitPrefix + classname + toolkitPostfix; std::list allGUIs = itk::ObjectFactoryBase::CreateAllInstance(guiClassname.c_str()); for (auto iter = allGUIs.begin(); iter != allGUIs.end(); ++iter) { if (object.IsNull()) { object = dynamic_cast(iter->GetPointer()); } else { MITK_ERROR << "There is more than one GUI for " << classname << " (several factories claim ability to produce a " << guiClassname << " ) " << std::endl; return nullptr; // people should see and fix this error } } return object; } mitk::NodePredicateBase::ConstPointer mitk::Tool::GetReferenceDataPreference() const { return m_PredicateReference.GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::Tool::GetWorkingDataPreference() const { return m_IsSegmentationPredicate.GetPointer(); } mitk::DataNode::Pointer mitk::Tool::CreateEmptySegmentationNode(const Image *original, const std::string &organName, const mitk::Color &color) const { // we NEED a reference image for size etc. if (!original) return nullptr; // actually create a new empty segmentation PixelType pixelType(mitk::MakeScalarPixelType()); LabelSetImage::Pointer segmentation = LabelSetImage::New(); if (original->GetDimension() == 2) { const unsigned int dimensions[] = {original->GetDimension(0), original->GetDimension(1), 1}; segmentation->Initialize(pixelType, 3, dimensions); segmentation->AddLayer(); } else { segmentation->Initialize(original); } mitk::Label::Pointer label = mitk::Label::New(); label->SetName(organName); label->SetColor(color); label->SetValue(1); segmentation->GetActiveLabelSet()->AddLabel(label); segmentation->GetActiveLabelSet()->SetActiveLabel(1); unsigned int byteSize = sizeof(mitk::Label::PixelType); if (segmentation->GetDimension() < 4) { for (unsigned int dim = 0; dim < segmentation->GetDimension(); ++dim) { byteSize *= segmentation->GetDimension(dim); } mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(0)); memset(writeAccess.GetData(), 0, byteSize); } else { // if we have a time-resolved image we need to set memory to 0 for each time step for (unsigned int dim = 0; dim < 3; ++dim) { byteSize *= segmentation->GetDimension(dim); } for (unsigned int volumeNumber = 0; volumeNumber < segmentation->GetDimension(3); volumeNumber++) { mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(volumeNumber)); memset(writeAccess.GetData(), 0, byteSize); } } if (original->GetTimeGeometry()) { TimeGeometry::Pointer originalGeometry = original->GetTimeGeometry()->Clone(); segmentation->SetTimeGeometry(originalGeometry); } else { Tool::ErrorMessage("Original image does not have a 'Time sliced geometry'! Cannot create a segmentation."); return nullptr; } return CreateSegmentationNode(segmentation, organName, color); } mitk::DataNode::Pointer mitk::Tool::CreateSegmentationNode(Image *image, const std::string &organName, const mitk::Color &color) const { if (!image) return nullptr; // decorate the datatreenode with some properties DataNode::Pointer segmentationNode = DataNode::New(); segmentationNode->SetData(image); // name segmentationNode->SetProperty("name", StringProperty::New(organName)); // visualization properties segmentationNode->SetProperty("binary", BoolProperty::New(true)); segmentationNode->SetProperty("color", ColorProperty::New(color)); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); lut->SetType(mitk::LookupTable::MULTILABEL); mitk::LookupTableProperty::Pointer lutProp = mitk::LookupTableProperty::New(); lutProp->SetLookupTable(lut); segmentationNode->SetProperty("LookupTable", lutProp); segmentationNode->SetProperty("texture interpolation", BoolProperty::New(false)); segmentationNode->SetProperty("layer", IntProperty::New(10)); segmentationNode->SetProperty("levelwindow", LevelWindowProperty::New(LevelWindow(0.5, 1))); segmentationNode->SetProperty("opacity", FloatProperty::New(0.3)); segmentationNode->SetProperty("segmentation", BoolProperty::New(true)); segmentationNode->SetProperty("reslice interpolation", VtkResliceInterpolationProperty::New()); // otherwise -> segmentation appears in 2 // slices sometimes (only visual effect, not // different data) // For MITK-3M3 release, the volume of all segmentations should be shown segmentationNode->SetProperty("showVolume", BoolProperty::New(true)); return segmentationNode; } us::ModuleResource mitk::Tool::GetIconResource() const { // Each specific tool should load its own resource. This one will be invalid return us::ModuleResource(); } us::ModuleResource mitk::Tool::GetCursorIconResource() const { // Each specific tool should load its own resource. This one will be invalid return us::ModuleResource(); } diff --git a/Modules/Segmentation/Interactions/mitkToolEvents.h b/Modules/Segmentation/Interactions/mitkToolEvents.h index 9f615ea63b..463b8acfa0 100644 --- a/Modules/Segmentation/Interactions/mitkToolEvents.h +++ b/Modules/Segmentation/Interactions/mitkToolEvents.h @@ -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. ============================================================================*/ #ifndef MITK_TOOL_EVENTS_H #define MITK_TOOL_EVENTS_H #pragma GCC visibility push(default) #include #pragma GCC visibility pop namespace mitk { /** \brief Basic tool event without any parameters Can simply be inherited using the itkEventMacro, e.g. \code namespace mitk { class MyTool : public Tool { public: itkEventMacro(MySpecialEvent, ToolEvent); [...] protected: // Invoke your event like this void YourExampleMethod() { InvokeEvent( MySpecialEvent() ); } }; } \endcode */ #pragma GCC visibility push(default) - itkEventMacro(ToolEvent, itk::ModifiedEvent); + itkEventMacroDeclaration(ToolEvent, itk::ModifiedEvent); // Definition is in mitkTool.cpp #pragma GCC visibility pop /** \brief Tool event with 1 parameter Can store one parameter for use within an observer. To derive your own special events, use the mitkToolEventMacro1Param macro. \code namespace mitk { class MyTool : public Tool { public: mitkToolEventMacro1Param(FooToolEvent, int); [...] protected: // Invoke your event like this void YourExampleMethod() { InvokeEvent( FooToolEvent(32) ); } }; } \endcode */ template class ParameterToolEvent : public ToolEvent { public: typedef ParameterToolEvent Self; typedef ToolEvent Superclass; ParameterToolEvent(const T parameter) : m_Parameter(parameter) {} ParameterToolEvent(const Self &s) : ToolEvent(s), m_Parameter(s.m_Parameter) {} ~ParameterToolEvent() override {} const char *GetEventName() const override { return "ParameterToolEvent"; } bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast(e); } ::itk::EventObject *MakeObject() const override { return new Self(m_Parameter); } const T GetParameter() const { return m_Parameter; } protected: const T m_Parameter; private: ParameterToolEvent(); void operator=(const Self &); }; /** \brief Tool event with 1 parameter Can store one parameter for use within an observer. To derive your own special events, use the mitkToolEventMacro1Param macro. \code namespace mitk { class MyTool : public Tool { public: mitkToolEventMacro1Param(FooToolEvent, int); [...] protected: // Invoke your event like this void YourExampleMethod() { InvokeEvent( BarToolEvent(32, false) ); } }; } \endcode */ template class TwoParameterToolEvent : public ToolEvent { public: typedef TwoParameterToolEvent Self; typedef ToolEvent Superclass; TwoParameterToolEvent(const T parameter1, const U parameter2) : m_Parameter1(parameter1), m_Parameter2(parameter2) { } TwoParameterToolEvent(const Self &s) : ToolEvent(s), m_Parameter1(s.m_Parameter1), m_Parameter2(s.m_Parameter2) {} ~TwoParameterToolEvent() override {} const char *GetEventName() const override { return "TwoParameterToolEvent"; } bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast(e); } ::itk::EventObject *MakeObject() const override { return new Self(m_Parameter1, m_Parameter2); } const T GetParameter1() const { return m_Parameter1; } const T GetParameter2() const { return m_Parameter2; } protected: const T m_Parameter1; const U m_Parameter2; private: TwoParameterToolEvent(); void operator=(const Self &); }; typedef ParameterToolEvent IntegerToolEvent; typedef ParameterToolEvent FloatToolEvent; typedef ParameterToolEvent BoolToolEvent; } // namespace // some macros to let tools define their own event classes as inner classes (should then inherit from something like // FloatToolEvent // inheritance, because it allows observers to distinguish events #define mitkToolEventMacro(eventname, baseevent) \ \ class eventname : public baseevent\ {virtual const char * GetEventName() const {return #eventname; \ } \ \ } \ ; #define mitkToolEventMacro1Param(eventname, paramtype1) \ \ class eventname : public ParameterToolEvent \ \ { \ public: \ virtual const char *GetEventName() const { return #eventname "(" #paramtype1 ")"; } \ eventname(const paramtype1 parameter) : ParameterToolEvent(parameter) {} \ private: \ eventname(); \ \ }; #define mitkToolEventMacro2Param(eventname, paramtype1, paramtype2) \ \ class eventname : public TwoParameterToolEvent \ \ { \ public: \ virtual const char *GetEventName() const { return #eventname "(" #paramtype1 "," #paramtype2 ")"; } \ eventname(const paramtype1 parameter1, const paramtype2 parameter2) \ : TwoParameterToolEvent(parameter1, parameter2) \ { \ } \ \ private: \ eventname(); \ \ }; #endif diff --git a/Modules/Segmentation/Interactions/mitkWatershedTool.cpp b/Modules/Segmentation/Interactions/mitkWatershedTool.cpp index b533bbe242..358cade477 100644 --- a/Modules/Segmentation/Interactions/mitkWatershedTool.cpp +++ b/Modules/Segmentation/Interactions/mitkWatershedTool.cpp @@ -1,163 +1,163 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkWatershedTool.h" #include "mitkIOUtil.h" #include "mitkITKImageImport.h" #include "mitkImage.h" #include "mitkLabelSetImage.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageStatisticsHolder.h" #include "mitkLevelWindowManager.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkProgressBar.h" #include "mitkRenderingManager.h" #include "mitkRenderingModeProperty.h" #include "mitkToolCommand.h" #include "mitkToolManager.h" #include #include #include #include #include #include -#include +#include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, WatershedTool, "Watershed tool"); } void mitk::WatershedTool::Activated() { Superclass::Activated(); m_Level = 0.0; m_Threshold = 0.0; m_MagFilter = nullptr; m_WatershedFilter = nullptr; m_LastFilterInput = nullptr; } us::ModuleResource mitk::WatershedTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Watershed_48x48.png"); return resource; } const char **mitk::WatershedTool::GetXPM() const { return nullptr; } const char *mitk::WatershedTool::GetName() const { return "Watershed"; } mitk::LabelSetImage::Pointer mitk::WatershedTool::ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType /*timeStep*/) { mitk::LabelSetImage::Pointer labelSetOutput; try { mitk::Image::Pointer output; bool inputChanged = inputAtTimeStep != m_LastFilterInput; // create and run itk filter pipeline AccessByItk_2(inputAtTimeStep, ITKWatershed, output, inputChanged); labelSetOutput = mitk::LabelSetImage::New(); labelSetOutput->InitializeByLabeledImage(output); } catch (itk::ExceptionObject & e) { //force reset of filters as they might be in an invalid state now. m_MagFilter = nullptr; m_WatershedFilter = nullptr; m_LastFilterInput = nullptr; MITK_ERROR << "Watershed Filter Error: " << e.GetDescription(); } m_LastFilterInput = inputAtTimeStep; return labelSetOutput; } template void mitk::WatershedTool::ITKWatershed(const itk::Image* originalImage, mitk::Image::Pointer& segmentation, bool inputChanged) { typedef itk::WatershedImageFilter> WatershedFilter; typedef itk::GradientMagnitudeRecursiveGaussianImageFilter, itk::Image> MagnitudeFilter; // We create the filter pipeline only once (if needed) and not everytime we // generate the ml image preview. // Reason: If only the levels are changed the update of the pipe line is very // fast and we want to profit from this feature. // at first add a gradient magnitude filter typename MagnitudeFilter::Pointer magnitude = dynamic_cast(m_MagFilter.GetPointer()); if (magnitude.IsNull()) { magnitude = MagnitudeFilter::New(); magnitude->SetSigma(1.0); magnitude->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_MagFilter = magnitude.GetPointer(); } if (inputChanged) { magnitude->SetInput(originalImage); } // then add the watershed filter to the pipeline typename WatershedFilter::Pointer watershed = dynamic_cast(m_WatershedFilter.GetPointer()); if (watershed.IsNull()) { watershed = WatershedFilter::New(); watershed->SetInput(magnitude->GetOutput()); watershed->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_WatershedFilter = watershed.GetPointer(); } watershed->SetThreshold(m_Threshold); watershed->SetLevel(m_Level); watershed->Update(); // then make sure, that the output has the desired pixel type typedef itk::CastImageFilter> CastFilter; typename CastFilter::Pointer cast = CastFilter::New(); cast->SetInput(watershed->GetOutput()); // start the whole pipeline cast->Update(); // since we obtain a new image from our pipeline, we have to make sure, that our mitk::Image::Pointer // is responsible for the memory management of the output image segmentation = mitk::GrabItkImageMemory(cast->GetOutput()); } diff --git a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp index 6bf2ba52ed..2808f95126 100644 --- a/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp +++ b/Modules/Segmentation/SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp @@ -1,185 +1,185 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkBooleanOperation.h" #include #include #include #include #include #include typedef itk::Image ImageType; static mitk::Image::Pointer Get3DSegmentation(mitk::Image::Pointer segmentation, mitk::TimePointType time) { if (segmentation->GetDimension() != 4) return segmentation; auto imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(segmentation); imageTimeSelector->SetTimeNr(segmentation->GetTimeGeometry()->TimePointToTimeStep(time)); imageTimeSelector->UpdateLargestPossibleRegion(); return imageTimeSelector->GetOutput(); } static ImageType::Pointer CastTo3DItkImage(mitk::Image::Pointer segmentation, mitk::TimePointType time) { ImageType::Pointer result; mitk::CastToItkImage(Get3DSegmentation(segmentation, time), result); return result; } mitk::BooleanOperation::BooleanOperation(Type type, mitk::Image::Pointer segmentationA, mitk::Image::Pointer segmentationB, TimePointType time) : m_Type(type), m_SegmentationA(segmentationA), m_SegmentationB(segmentationB), m_TimePoint(time) { this->ValidateSegmentations(); } mitk::BooleanOperation::~BooleanOperation() { } mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetResult() const { switch (m_Type) { case Difference: return this->GetDifference(); case Intersection: return this->GetIntersection(); case Union: return this->GetUnion(); default: mitkThrow() << "Unknown boolean operation type '" << m_Type << "'!"; } } mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetDifference() const { auto input1 = CastTo3DItkImage(m_SegmentationA, m_TimePoint); auto input2 = CastTo3DItkImage(m_SegmentationB, m_TimePoint); auto notFilter = itk::NotImageFilter::New(); notFilter->SetInput(input2); auto andFilter = itk::AndImageFilter::New(); andFilter->SetInput1(input1); andFilter->SetInput2(notFilter->GetOutput()); andFilter->UpdateLargestPossibleRegion(); auto tempResult = Image::New(); CastToMitkImage(andFilter->GetOutput(), tempResult); tempResult->DisconnectPipeline(); auto result = mitk::LabelSetImage::New(); result->InitializeByLabeledImage(tempResult); return result; } mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetIntersection() const { auto input1 = CastTo3DItkImage(m_SegmentationA, m_TimePoint); auto input2 = CastTo3DItkImage(m_SegmentationB, m_TimePoint); auto andFilter = itk::AndImageFilter::New(); andFilter->SetInput1(input1); andFilter->SetInput2(input2); andFilter->UpdateLargestPossibleRegion(); auto tempResult = Image::New(); CastToMitkImage(andFilter->GetOutput(), tempResult); tempResult->DisconnectPipeline(); auto result = mitk::LabelSetImage::New(); result->InitializeByLabeledImage(tempResult); return result; } mitk::LabelSetImage::Pointer mitk::BooleanOperation::GetUnion() const { auto input1 = CastTo3DItkImage(m_SegmentationA, m_TimePoint); auto input2 = CastTo3DItkImage(m_SegmentationB, m_TimePoint); auto orFilter = itk::OrImageFilter::New(); orFilter->SetInput1(input1); orFilter->SetInput2(input2); orFilter->UpdateLargestPossibleRegion(); auto tempResult = Image::New(); CastToMitkImage(orFilter->GetOutput(), tempResult); tempResult->DisconnectPipeline(); auto result = mitk::LabelSetImage::New(); result->InitializeByLabeledImage(tempResult); return result; } void mitk::BooleanOperation::ValidateSegmentation(mitk::Image::Pointer segmentation) const { if (segmentation.IsNull()) mitkThrow() << "Segmentation is nullptr!"; if (segmentation->GetImageDescriptor()->GetNumberOfChannels() != 1) mitkThrow() << "Segmentation has more than one channel!"; auto pixelType = segmentation->GetImageDescriptor()->GetChannelDescriptor().GetPixelType(); - if (pixelType.GetPixelType() != itk::ImageIOBase::SCALAR || - (pixelType.GetComponentType() != itk::ImageIOBase::UCHAR && - pixelType.GetComponentType() != itk::ImageIOBase::USHORT)) + if (pixelType.GetPixelType() != itk::IOPixelEnum::SCALAR || + (pixelType.GetComponentType() != itk::IOComponentEnum::UCHAR && + pixelType.GetComponentType() != itk::IOComponentEnum::USHORT)) mitkThrow() << "Segmentation is neither of type 'unsigned char' nor type 'unsigned short'!"; auto dimension = segmentation->GetDimension(); if (dimension > 4) mitkThrow() << "Segmentation has more than four dimensions!"; if (dimension < 3) mitkThrow() << "Segmentation has less than three dimensions!"; if (!segmentation->GetTimeGeometry()->IsValidTimePoint(m_TimePoint)) mitkThrow() << "Segmentation is not defined for specified time point. Time point: " << m_TimePoint; } void mitk::BooleanOperation::ValidateSegmentations() const { this->ValidateSegmentation(m_SegmentationA); this->ValidateSegmentation(m_SegmentationB); if (m_SegmentationA->GetDimension() != m_SegmentationB->GetDimension()) mitkThrow() << "Segmentations have different dimensions!"; const auto geometryA = m_SegmentationA->GetTimeGeometry()->GetGeometryForTimePoint(m_TimePoint); const auto geometryB = m_SegmentationB->GetTimeGeometry()->GetGeometryForTimePoint(m_TimePoint); if (!mitk::Equal(*(geometryA.GetPointer()), *(geometryB.GetPointer()),eps,false)) { mitkThrow() << "Segmentations have different geometries and cannot be used for boolean operations!"; } } diff --git a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp index 0090146b9c..e41f25d83b 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkSlicesInterpolator.cpp @@ -1,1442 +1,1442 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "QmitkSlicesInterpolator.h" #include "QmitkSelectableGLWidget.h" #include "QmitkStdMultiWidget.h" #include "mitkApplyDiffImageOperation.h" #include "mitkColorProperty.h" #include "mitkCoreObjectFactory.h" #include "mitkDiffImageApplier.h" #include "mitkInteractionConst.h" #include "mitkLevelWindowProperty.h" #include "mitkOperationEvent.h" #include "mitkProgressBar.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkSegTool2D.h" #include "mitkSliceNavigationController.h" #include "mitkSurfaceToImageFilter.h" #include "mitkToolManager.h" #include "mitkUndoController.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { template itk::SmartPointer GetData(const mitk::DataNode* dataNode) { return nullptr != dataNode ? dynamic_cast(dataNode->GetData()) : nullptr; } } float SURFACE_COLOR_RGB[3] = {0.49f, 1.0f, 0.16f}; const std::map QmitkSlicesInterpolator::createActionToSliceDimension() { std::map actionToSliceDimension; foreach (mitk::SliceNavigationController *slicer, m_ControllerToDeleteObserverTag.keys()) { actionToSliceDimension[new QAction(QString::fromStdString(slicer->GetViewDirectionAsString()), nullptr)] = slicer; } return actionToSliceDimension; } QmitkSlicesInterpolator::QmitkSlicesInterpolator(QWidget *parent, const char * /*name*/) : QWidget(parent), // ACTION_TO_SLICEDIMENSION( createActionToSliceDimension() ), m_Interpolator(mitk::SegmentationInterpolationController::New()), m_SurfaceInterpolator(mitk::SurfaceInterpolationController::GetInstance()), m_ToolManager(nullptr), m_Initialized(false), m_LastSNC(nullptr), m_LastSliceIndex(0), m_2DInterpolationEnabled(false), m_3DInterpolationEnabled(false), m_FirstRun(true) { m_GroupBoxEnableExclusiveInterpolationMode = new QGroupBox("Interpolation", this); QVBoxLayout *vboxLayout = new QVBoxLayout(m_GroupBoxEnableExclusiveInterpolationMode); m_EdgeDetector = mitk::FeatureBasedEdgeDetectionFilter::New(); m_PointScorer = mitk::PointCloudScoringFilter::New(); m_CmbInterpolation = new QComboBox(m_GroupBoxEnableExclusiveInterpolationMode); m_CmbInterpolation->addItem("Disabled"); m_CmbInterpolation->addItem("2-Dimensional"); m_CmbInterpolation->addItem("3-Dimensional"); vboxLayout->addWidget(m_CmbInterpolation); m_BtnApply2D = new QPushButton("Confirm for single slice", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApply2D); m_BtnApplyForAllSlices2D = new QPushButton("Confirm for all slices", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApplyForAllSlices2D); m_BtnApply3D = new QPushButton("Confirm", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnApply3D); // T28261 // m_BtnSuggestPlane = new QPushButton("Suggest a plane", m_GroupBoxEnableExclusiveInterpolationMode); // vboxLayout->addWidget(m_BtnSuggestPlane); m_BtnReinit3DInterpolation = new QPushButton("Reinit Interpolation", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_BtnReinit3DInterpolation); m_ChkShowPositionNodes = new QCheckBox("Show Position Nodes", m_GroupBoxEnableExclusiveInterpolationMode); vboxLayout->addWidget(m_ChkShowPositionNodes); this->HideAllInterpolationControls(); connect(m_CmbInterpolation, SIGNAL(currentIndexChanged(int)), this, SLOT(OnInterpolationMethodChanged(int))); connect(m_BtnApply2D, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked())); connect(m_BtnApplyForAllSlices2D, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked())); connect(m_BtnApply3D, SIGNAL(clicked()), this, SLOT(OnAccept3DInterpolationClicked())); // T28261 // connect(m_BtnSuggestPlane, SIGNAL(clicked()), this, SLOT(OnSuggestPlaneClicked())); connect(m_BtnReinit3DInterpolation, SIGNAL(clicked()), this, SLOT(OnReinit3DInterpolation())); connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool))); connect(m_ChkShowPositionNodes, SIGNAL(toggled(bool)), this, SIGNAL(SignalShowMarkerNodes(bool))); QHBoxLayout *layout = new QHBoxLayout(this); layout->addWidget(m_GroupBoxEnableExclusiveInterpolationMode); this->setLayout(layout); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnInterpolationInfoChanged); InterpolationInfoChangedObserverTag = m_Interpolator->AddObserver(itk::ModifiedEvent(), command); itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged); SurfaceInterpolationInfoChangedObserverTag = m_SurfaceInterpolator->AddObserver(itk::ModifiedEvent(), command2); auto command3 = itk::ReceptorMemberCommand::New(); command3->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnInterpolationAborted); InterpolationAbortedObserverTag = m_Interpolator->AddObserver(itk::AbortEvent(), command3); // feedback node and its visualization properties m_FeedbackNode = mitk::DataNode::New(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties(m_FeedbackNode); m_FeedbackNode->SetProperty("binary", mitk::BoolProperty::New(true)); m_FeedbackNode->SetProperty("outline binary", mitk::BoolProperty::New(true)); m_FeedbackNode->SetProperty("color", mitk::ColorProperty::New(255.0, 255.0, 0.0)); m_FeedbackNode->SetProperty("texture interpolation", mitk::BoolProperty::New(false)); m_FeedbackNode->SetProperty("layer", mitk::IntProperty::New(20)); m_FeedbackNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, 1))); m_FeedbackNode->SetProperty("name", mitk::StringProperty::New("Interpolation feedback")); m_FeedbackNode->SetProperty("opacity", mitk::FloatProperty::New(0.8)); m_FeedbackNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_InterpolatedSurfaceNode = mitk::DataNode::New(); m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB)); m_InterpolatedSurfaceNode->SetProperty("name", mitk::StringProperty::New("Surface Interpolation feedback")); m_InterpolatedSurfaceNode->SetProperty("opacity", mitk::FloatProperty::New(0.5)); m_InterpolatedSurfaceNode->SetProperty("line width", mitk::FloatProperty::New(4.0f)); m_InterpolatedSurfaceNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_InterpolatedSurfaceNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode = mitk::DataNode::New(); m_3DContourNode->SetProperty("color", mitk::ColorProperty::New(0.0, 0.0, 0.0)); m_3DContourNode->SetProperty("hidden object", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty("name", mitk::StringProperty::New("Drawn Contours")); m_3DContourNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); m_3DContourNode->SetProperty("material.wireframeLineWidth", mitk::FloatProperty::New(2.0f)); m_3DContourNode->SetProperty("3DContourContainer", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_3DContourNode->SetVisibility( false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget0"))); m_3DContourNode->SetVisibility( false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); m_3DContourNode->SetVisibility( false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))); m_3DContourNode->SetVisibility( false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))); QWidget::setContentsMargins(0, 0, 0, 0); if (QWidget::layout() != nullptr) { QWidget::layout()->setContentsMargins(0, 0, 0, 0); } // For running 3D Interpolation in background // create a QFuture and a QFutureWatcher connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(OnSurfaceInterpolationFinished())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer())); m_Timer = new QTimer(this); connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor())); } void QmitkSlicesInterpolator::SetDataStorage(mitk::DataStorage::Pointer storage) { if (m_DataStorage == storage) { return; } if (m_DataStorage.IsNotNull()) { m_DataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkSlicesInterpolator::NodeRemoved) ); } m_DataStorage = storage; m_SurfaceInterpolator->SetDataStorage(storage); if (m_DataStorage.IsNotNull()) { m_DataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkSlicesInterpolator::NodeRemoved) ); } } mitk::DataStorage *QmitkSlicesInterpolator::GetDataStorage() { if (m_DataStorage.IsNotNull()) { return m_DataStorage; } else { return nullptr; } } void QmitkSlicesInterpolator::Initialize(mitk::ToolManager *toolManager, const QList &controllers) { Q_ASSERT(!controllers.empty()); if (m_Initialized) { // remove old observers Uninitialize(); } m_ToolManager = toolManager; if (m_ToolManager) { // set enabled only if a segmentation is selected mitk::DataNode *node = m_ToolManager->GetWorkingData(0); QWidget::setEnabled(node != nullptr); // react whenever the set of selected segmentation changes m_ToolManager->WorkingDataChanged += mitk::MessageDelegate(this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified); m_ToolManager->ReferenceDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified); // connect to the slice navigation controller. after each change, call the interpolator foreach (mitk::SliceNavigationController *slicer, controllers) { // Has to be initialized m_LastSNC = slicer; m_TimePoints.insert(slicer, slicer->GetSelectedTimePoint()); itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted); m_ControllerToDeleteObserverTag.insert(slicer, slicer->AddObserver(itk::DeleteEvent(), deleteCommand)); itk::MemberCommand::Pointer timeChangedCommand = itk::MemberCommand::New(); timeChangedCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnTimeChanged); m_ControllerToTimeObserverTag.insert( slicer, slicer->AddObserver(mitk::SliceNavigationController::TimeGeometryEvent(nullptr, 0), timeChangedCommand)); itk::MemberCommand::Pointer sliceChangedCommand = itk::MemberCommand::New(); sliceChangedCommand->SetCallbackFunction(this, &QmitkSlicesInterpolator::OnSliceChanged); m_ControllerToSliceObserverTag.insert( slicer, slicer->AddObserver(mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0), sliceChangedCommand)); } ACTION_TO_SLICEDIMENSION = createActionToSliceDimension(); } m_Initialized = true; } void QmitkSlicesInterpolator::Uninitialize() { if (m_ToolManager.IsNotNull()) { m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate(this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified); m_ToolManager->ReferenceDataChanged -= mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified); } foreach (mitk::SliceNavigationController *slicer, m_ControllerToSliceObserverTag.keys()) { slicer->RemoveObserver(m_ControllerToDeleteObserverTag.take(slicer)); slicer->RemoveObserver(m_ControllerToTimeObserverTag.take(slicer)); slicer->RemoveObserver(m_ControllerToSliceObserverTag.take(slicer)); } ACTION_TO_SLICEDIMENSION.clear(); m_ToolManager = nullptr; m_Initialized = false; } QmitkSlicesInterpolator::~QmitkSlicesInterpolator() { if (m_Initialized) { // remove old observers Uninitialize(); } WaitForFutures(); if (m_DataStorage.IsNotNull()) { m_DataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkSlicesInterpolator::NodeRemoved) ); if (m_DataStorage->Exists(m_3DContourNode)) m_DataStorage->Remove(m_3DContourNode); if (m_DataStorage->Exists(m_InterpolatedSurfaceNode)) m_DataStorage->Remove(m_InterpolatedSurfaceNode); } // remove observer m_Interpolator->RemoveObserver(InterpolationAbortedObserverTag); m_Interpolator->RemoveObserver(InterpolationInfoChangedObserverTag); m_SurfaceInterpolator->RemoveObserver(SurfaceInterpolationInfoChangedObserverTag); delete m_Timer; } /** External enableization... */ void QmitkSlicesInterpolator::setEnabled(bool enable) { QWidget::setEnabled(enable); // Set the gui elements of the different interpolation modi enabled if (enable) { if (m_2DInterpolationEnabled) { this->Show2DInterpolationControls(true); m_Interpolator->Activate2DInterpolation(true); } else if (m_3DInterpolationEnabled) { this->Show3DInterpolationControls(true); this->Show3DInterpolationResult(true); } } // Set all gui elements of the interpolation disabled else { this->HideAllInterpolationControls(); this->Show3DInterpolationResult(false); } } void QmitkSlicesInterpolator::On2DInterpolationEnabled(bool status) { OnInterpolationActivated(status); m_Interpolator->Activate2DInterpolation(status); } void QmitkSlicesInterpolator::On3DInterpolationEnabled(bool status) { On3DInterpolationActivated(status); } void QmitkSlicesInterpolator::OnInterpolationDisabled(bool status) { if (status) { OnInterpolationActivated(!status); On3DInterpolationActivated(!status); this->Show3DInterpolationResult(false); } } void QmitkSlicesInterpolator::HideAllInterpolationControls() { this->Show2DInterpolationControls(false); this->Show3DInterpolationControls(false); } void QmitkSlicesInterpolator::Show2DInterpolationControls(bool show) { m_BtnApply2D->setVisible(show); m_BtnApplyForAllSlices2D->setVisible(show); } void QmitkSlicesInterpolator::Show3DInterpolationControls(bool show) { m_BtnApply3D->setVisible(show); // T28261 // m_BtnSuggestPlane->setVisible(show); m_ChkShowPositionNodes->setVisible(show); m_BtnReinit3DInterpolation->setVisible(show); } void QmitkSlicesInterpolator::OnInterpolationMethodChanged(int index) { switch (index) { case 0: // Disabled m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation"); this->HideAllInterpolationControls(); this->OnInterpolationActivated(false); this->On3DInterpolationActivated(false); this->Show3DInterpolationResult(false); m_Interpolator->Activate2DInterpolation(false); break; case 1: // 2D m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)"); this->HideAllInterpolationControls(); this->Show2DInterpolationControls(true); this->OnInterpolationActivated(true); this->On3DInterpolationActivated(false); m_Interpolator->Activate2DInterpolation(true); break; case 2: // 3D m_GroupBoxEnableExclusiveInterpolationMode->setTitle("Interpolation (Enabled)"); this->HideAllInterpolationControls(); this->Show3DInterpolationControls(true); this->OnInterpolationActivated(false); this->On3DInterpolationActivated(true); m_Interpolator->Activate2DInterpolation(false); break; default: MITK_ERROR << "Unknown interpolation method!"; m_CmbInterpolation->setCurrentIndex(0); break; } } void QmitkSlicesInterpolator::OnShowMarkers(bool state) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = m_DataStorage->GetSubset(mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true))); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state)); } } void QmitkSlicesInterpolator::OnToolManagerWorkingDataModified() { if (m_ToolManager->GetWorkingData(0) != nullptr) { m_Segmentation = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); m_BtnReinit3DInterpolation->setEnabled(true); } else { // If no workingdata is set, remove the interpolation feedback this->GetDataStorage()->Remove(m_FeedbackNode); m_FeedbackNode->SetData(nullptr); this->GetDataStorage()->Remove(m_3DContourNode); m_3DContourNode->SetData(nullptr); this->GetDataStorage()->Remove(m_InterpolatedSurfaceNode); m_InterpolatedSurfaceNode->SetData(nullptr); m_BtnReinit3DInterpolation->setEnabled(false); return; } // Updating the current selected segmentation for the 3D interpolation SetCurrentContourListID(); if (m_2DInterpolationEnabled) { OnInterpolationActivated(true); // re-initialize if needed } this->CheckSupportedImageDimension(); } void QmitkSlicesInterpolator::OnToolManagerReferenceDataModified() { } void QmitkSlicesInterpolator::OnTimeChanged(itk::Object *sender, const itk::EventObject &e) { // Check if we really have a GeometryTimeEvent if (!dynamic_cast(&e)) return; mitk::SliceNavigationController *slicer = dynamic_cast(sender); Q_ASSERT(slicer); const auto timePoint = slicer->GetSelectedTimePoint(); m_TimePoints[slicer] = timePoint; m_SurfaceInterpolator->SetCurrentTimePoint(timePoint); if (m_LastSNC == slicer) { slicer->SendSlice(); // will trigger a new interpolation } } void QmitkSlicesInterpolator::OnSliceChanged(itk::Object *sender, const itk::EventObject &e) { // Check whether we really have a GeometrySliceEvent if (!dynamic_cast(&e)) return; mitk::SliceNavigationController *slicer = dynamic_cast(sender); if (TranslateAndInterpolateChangedSlice(e, slicer)) { slicer->GetRenderer()->RequestUpdate(); } } bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const itk::EventObject &e, mitk::SliceNavigationController *slicer) { if (!m_2DInterpolationEnabled) return false; try { const mitk::SliceNavigationController::GeometrySliceEvent &event = dynamic_cast(e); mitk::TimeGeometry *tsg = event.GetTimeGeometry(); if (tsg && m_TimePoints.contains(slicer) && tsg->IsValidTimePoint(m_TimePoints[slicer])) { mitk::SlicedGeometry3D *slicedGeometry = dynamic_cast(tsg->GetGeometryForTimePoint(m_TimePoints[slicer]).GetPointer()); if (slicedGeometry) { m_LastSNC = slicer; mitk::PlaneGeometry *plane = dynamic_cast(slicedGeometry->GetPlaneGeometry(event.GetPos())); if (plane) Interpolate(plane, m_TimePoints[slicer], slicer); return true; } } } catch (const std::bad_cast &) { return false; // so what } return false; } void QmitkSlicesInterpolator::Interpolate(mitk::PlaneGeometry *plane, mitk::TimePointType timePoint, mitk::SliceNavigationController *slicer) { if (m_ToolManager) { mitk::DataNode *node = m_ToolManager->GetWorkingData(0); if (node) { m_Segmentation = dynamic_cast(node->GetData()); if (m_Segmentation) { if (!m_Segmentation->GetTimeGeometry()->IsValidTimePoint(timePoint)) { MITK_WARN << "Cannot interpolate segmentation. Passed time point is not within the time bounds of WorkingImage. Time point: " << timePoint; return; } const auto timeStep = m_Segmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint); int clickedSliceDimension(-1); int clickedSliceIndex(-1); // calculate real slice position, i.e. slice of the image mitk::SegTool2D::DetermineAffectedImageSlice(m_Segmentation, plane, clickedSliceDimension, clickedSliceIndex); mitk::Image::Pointer interpolation = m_Interpolator->Interpolate(clickedSliceDimension, clickedSliceIndex, plane, timeStep); m_FeedbackNode->SetData(interpolation); m_LastSNC = slicer; m_LastSliceIndex = clickedSliceIndex; } } } } void QmitkSlicesInterpolator::OnSurfaceInterpolationFinished() { mitk::Surface::Pointer interpolatedSurface = m_SurfaceInterpolator->GetInterpolationResult(); mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); if (interpolatedSurface.IsNotNull() && workingNode && workingNode->IsVisible( mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2")))) { m_BtnApply3D->setEnabled(true); // T28261 // m_BtnSuggestPlane->setEnabled(true); m_InterpolatedSurfaceNode->SetData(interpolatedSurface); m_3DContourNode->SetData(m_SurfaceInterpolator->GetContoursAsSurface()); this->Show3DInterpolationResult(true); if (!m_DataStorage->Exists(m_InterpolatedSurfaceNode)) { m_DataStorage->Add(m_InterpolatedSurfaceNode); } if (!m_DataStorage->Exists(m_3DContourNode)) { m_DataStorage->Add(m_3DContourNode, workingNode); } } else if (interpolatedSurface.IsNull()) { m_BtnApply3D->setEnabled(false); // T28261 // m_BtnSuggestPlane->setEnabled(false); if (m_DataStorage->Exists(m_InterpolatedSurfaceNode)) { this->Show3DInterpolationResult(false); } } m_BtnReinit3DInterpolation->setEnabled(true); foreach (mitk::SliceNavigationController *slicer, m_ControllerToTimeObserverTag.keys()) { slicer->GetRenderer()->RequestUpdate(); } } void QmitkSlicesInterpolator::OnAcceptInterpolationClicked() { if (m_Segmentation && m_FeedbackNode->GetData()) { // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk // reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // Set slice as input mitk::Image::Pointer slice = dynamic_cast(m_FeedbackNode->GetData()); reslice->SetInputSlice(slice->GetSliceData()->GetVtkImageAccessor(slice)->GetVtkImageData()); // set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); const auto timePoint = m_LastSNC->GetSelectedTimePoint(); if (!m_Segmentation->GetTimeGeometry()->IsValidTimePoint(timePoint)) { MITK_WARN << "Cannot accept interpolation. Time point selected by SliceNavigationController is not within the time bounds of segmentation. Time point: " << timePoint; return; } mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput(m_Segmentation); const auto timeStep = m_Segmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint); extractor->SetTimeStep(timeStep); extractor->SetWorldGeometry(m_LastSNC->GetCurrentPlaneGeometry()); extractor->SetVtkOutputRequest(true); extractor->SetResliceTransformByGeometry(m_Segmentation->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); extractor->Modified(); extractor->Update(); // the image was modified within the pipeline, but not marked so m_Segmentation->Modified(); m_Segmentation->GetVtkImageData()->Modified(); m_FeedbackNode->SetData(nullptr); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::AcceptAllInterpolations(mitk::SliceNavigationController *slicer) { /* * What exactly is done here: * 1. We create an empty diff image for the current segmentation * 2. All interpolated slices are written into the diff image * 3. Then the diffimage is applied to the original segmentation */ if (m_Segmentation) { mitk::Image::Pointer segmentation3D = m_Segmentation; unsigned int timeStep = 0; const auto timePoint = slicer->GetSelectedTimePoint(); if (4 == m_Segmentation->GetDimension()) { const auto* geometry = m_Segmentation->GetTimeGeometry(); if (!geometry->IsValidTimePoint(timePoint)) { MITK_WARN << "Cannot accept all interpolations. Time point selected by passed SliceNavigationController is not within the time bounds of segmentation. Time point: " << timePoint; return; } timeStep = geometry->TimePointToTimeStep(timePoint); auto timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(m_Segmentation); timeSelector->SetTimeNr(timeStep); timeSelector->Update(); segmentation3D = timeSelector->GetOutput(); } // Create an empty diff image for the undo operation auto diffImage = mitk::Image::New(); diffImage->Initialize(segmentation3D); // Create scope for ImageWriteAccessor so that the accessor is destroyed right after use { mitk::ImageWriteAccessor accessor(diffImage); // Set all pixels to zero auto pixelType = mitk::MakeScalarPixelType(); // For legacy purpose support former pixel type of segmentations (before multilabel) - if (itk::ImageIOBase::UCHAR == m_Segmentation->GetImageDescriptor()->GetChannelDescriptor().GetPixelType().GetComponentType()) + if (itk::IOComponentEnum::UCHAR == m_Segmentation->GetImageDescriptor()->GetChannelDescriptor().GetPixelType().GetComponentType()) pixelType = mitk::MakeScalarPixelType(); memset(accessor.GetData(), 0, pixelType.GetSize() * diffImage->GetDimension(0) * diffImage->GetDimension(1) * diffImage->GetDimension(2)); } // Since we need to shift the plane it must be clone so that the original plane isn't altered auto slicedGeometry = m_Segmentation->GetSlicedGeometry(); auto planeGeometry = slicer->GetCurrentPlaneGeometry()->Clone(); int sliceDimension = -1; int sliceIndex = -1; mitk::SegTool2D::DetermineAffectedImageSlice(m_Segmentation, planeGeometry, sliceDimension, sliceIndex); const auto numSlices = m_Segmentation->GetDimension(sliceDimension); mitk::ProgressBar::GetInstance()->AddStepsToDo(numSlices); std::atomic_uint totalChangedSlices; // Reuse interpolation algorithm instance for each slice to cache boundary calculations auto algorithm = mitk::ShapeBasedInterpolationAlgorithm::New(); // Distribute slice interpolations to multiple threads const auto numThreads = std::min(std::thread::hardware_concurrency(), numSlices); std::vector> sliceIndices(numThreads); for (std::remove_const_t sliceIndex = 0; sliceIndex < numSlices; ++sliceIndex) sliceIndices[sliceIndex % numThreads].push_back(sliceIndex); std::vector threads; threads.reserve(numThreads); // This lambda will be executed by the threads auto interpolate = [=, &interpolator = m_Interpolator, &totalChangedSlices](unsigned int threadIndex) { auto clonedPlaneGeometry = planeGeometry->Clone(); auto origin = clonedPlaneGeometry->GetOrigin(); for (auto sliceIndex : sliceIndices[threadIndex]) { slicedGeometry->WorldToIndex(origin, origin); origin[sliceDimension] = sliceIndex; slicedGeometry->IndexToWorld(origin, origin); clonedPlaneGeometry->SetOrigin(origin); auto interpolation = interpolator->Interpolate(sliceDimension, sliceIndex, clonedPlaneGeometry, timeStep, algorithm); if (interpolation.IsNotNull()) { // Setting up the reslicing pipeline which allows us to write the interpolation results back into the image volume auto reslicer = vtkSmartPointer::New(); // Set overwrite mode to true to write back to the image volume reslicer->SetInputSlice(interpolation->GetSliceData()->GetVtkImageAccessor(interpolation)->GetVtkImageData()); reslicer->SetOverwriteMode(true); reslicer->Modified(); auto diffSliceWriter = mitk::ExtractSliceFilter::New(reslicer); diffSliceWriter->SetInput(diffImage); diffSliceWriter->SetTimeStep(0); diffSliceWriter->SetWorldGeometry(clonedPlaneGeometry); diffSliceWriter->SetVtkOutputRequest(true); diffSliceWriter->SetResliceTransformByGeometry(diffImage->GetTimeGeometry()->GetGeometryForTimeStep(0)); diffSliceWriter->Modified(); diffSliceWriter->Update(); ++totalChangedSlices; } mitk::ProgressBar::GetInstance()->Progress(); } }; m_Interpolator->EnableSliceImageCache(); for (std::remove_const_t threadIndex = 0; threadIndex < numThreads; ++threadIndex) threads.emplace_back(interpolate, threadIndex); // Run the interpolation for (auto& thread : threads) thread.join(); m_Interpolator->DisableSliceImageCache(); if (totalChangedSlices > 0) { // Create do/undo operations auto* doOp = new mitk::ApplyDiffImageOperation(mitk::OpTEST, m_Segmentation, diffImage, timeStep); auto* undoOp = new mitk::ApplyDiffImageOperation(mitk::OpTEST, m_Segmentation, diffImage, timeStep); undoOp->SetFactor(-1.0); auto comment = "Confirm all interpolations (" + std::to_string(totalChangedSlices) + ")"; auto* undoStackItem = new mitk::OperationEvent(mitk::DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, comment); mitk::OperationEvent::IncCurrGroupEventId(); mitk::OperationEvent::IncCurrObjectEventId(); mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent(undoStackItem); // Apply the changes to the original image mitk::DiffImageApplier::GetInstanceForUndo()->ExecuteOperation(doOp); } m_FeedbackNode->SetData(nullptr); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::FinishInterpolation(mitk::SliceNavigationController *slicer) { // this redirect is for calling from outside if (slicer == nullptr) OnAcceptAllInterpolationsClicked(); else AcceptAllInterpolations(slicer); } void QmitkSlicesInterpolator::OnAcceptAllInterpolationsClicked() { QMenu orientationPopup(this); std::map::const_iterator it; for (it = ACTION_TO_SLICEDIMENSION.begin(); it != ACTION_TO_SLICEDIMENSION.end(); it++) orientationPopup.addAction(it->first); connect(&orientationPopup, SIGNAL(triggered(QAction *)), this, SLOT(OnAcceptAllPopupActivated(QAction *))); orientationPopup.exec(QCursor::pos()); } void QmitkSlicesInterpolator::OnAccept3DInterpolationClicked() { auto referenceImage = GetData(m_ToolManager->GetReferenceData(0)); auto* segmentationDataNode = m_ToolManager->GetWorkingData(0); auto segmentation = GetData(segmentationDataNode); if (referenceImage.IsNull() || segmentation.IsNull()) return; const auto* segmentationGeometry = segmentation->GetTimeGeometry(); const auto timePoint = m_LastSNC->GetSelectedTimePoint(); if (!referenceImage->GetTimeGeometry()->IsValidTimePoint(timePoint) || !segmentationGeometry->IsValidTimePoint(timePoint)) { MITK_WARN << "Cannot accept interpolation. Current time point is not within the time bounds of the patient image and segmentation."; return; } auto interpolatedSurface = GetData(m_InterpolatedSurfaceNode); if (interpolatedSurface.IsNull()) return; auto surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); surfaceToImageFilter->SetImage(referenceImage); surfaceToImageFilter->SetMakeOutputBinary(true); - surfaceToImageFilter->SetUShortBinaryPixelType(itk::ImageIOBase::USHORT == segmentation->GetPixelType().GetComponentType()); + surfaceToImageFilter->SetUShortBinaryPixelType(itk::IOComponentEnum::USHORT == segmentation->GetPixelType().GetComponentType()); surfaceToImageFilter->SetInput(interpolatedSurface); surfaceToImageFilter->Update(); mitk::Image::Pointer interpolatedSegmentation = surfaceToImageFilter->GetOutput(); auto timeStep = interpolatedSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint); mitk::ImageReadAccessor readAccessor(interpolatedSegmentation, interpolatedSegmentation->GetVolumeData(timeStep)); const auto* dataPointer = readAccessor.GetData(); if (nullptr == dataPointer) return; timeStep = segmentationGeometry->TimePointToTimeStep(timePoint); segmentation->SetVolume(dataPointer, timeStep, 0); m_CmbInterpolation->setCurrentIndex(0); this->Show3DInterpolationResult(false); std::string name = segmentationDataNode->GetName() + "_3D-interpolation"; mitk::TimeBounds timeBounds; if (1 < interpolatedSurface->GetTimeSteps()) { name += "_t" + std::to_string(timeStep); auto* polyData = vtkPolyData::New(); polyData->DeepCopy(interpolatedSurface->GetVtkPolyData(timeStep)); auto surface = mitk::Surface::New(); surface->SetVtkPolyData(polyData); interpolatedSurface = surface; timeBounds = segmentationGeometry->GetTimeBounds(timeStep); } else { timeBounds = segmentationGeometry->GetTimeBounds(0); } auto* surfaceGeometry = static_cast(interpolatedSurface->GetTimeGeometry()); surfaceGeometry->SetFirstTimePoint(timeBounds[0]); surfaceGeometry->SetStepDuration(timeBounds[1] - timeBounds[0]); // Typical file formats for surfaces do not save any time-related information. As a workaround at least for MITK scene files, we have the // possibility to seralize this information as properties. interpolatedSurface->SetProperty("ProportionalTimeGeometry.FirstTimePoint", mitk::FloatProperty::New(surfaceGeometry->GetFirstTimePoint())); interpolatedSurface->SetProperty("ProportionalTimeGeometry.StepDuration", mitk::FloatProperty::New(surfaceGeometry->GetStepDuration())); auto interpolatedSurfaceDataNode = mitk::DataNode::New(); interpolatedSurfaceDataNode->SetData(interpolatedSurface); interpolatedSurfaceDataNode->SetName(name); interpolatedSurfaceDataNode->SetOpacity(0.7f); std::array rgb; segmentationDataNode->GetColor(rgb.data()); interpolatedSurfaceDataNode->SetColor(rgb.data()); m_DataStorage->Add(interpolatedSurfaceDataNode, segmentationDataNode); } void ::QmitkSlicesInterpolator::OnSuggestPlaneClicked() { if (m_PlaneWatcher.isRunning()) m_PlaneWatcher.waitForFinished(); m_PlaneFuture = QtConcurrent::run(this, &QmitkSlicesInterpolator::RunPlaneSuggestion); m_PlaneWatcher.setFuture(m_PlaneFuture); } void ::QmitkSlicesInterpolator::RunPlaneSuggestion() { if (m_FirstRun) mitk::ProgressBar::GetInstance()->AddStepsToDo(7); else mitk::ProgressBar::GetInstance()->AddStepsToDo(3); m_EdgeDetector->SetSegmentationMask(m_Segmentation); m_EdgeDetector->SetInput(dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData())); m_EdgeDetector->Update(); mitk::UnstructuredGrid::Pointer uGrid = mitk::UnstructuredGrid::New(); uGrid->SetVtkUnstructuredGrid(m_EdgeDetector->GetOutput()->GetVtkUnstructuredGrid()); mitk::ProgressBar::GetInstance()->Progress(); mitk::Surface::Pointer surface = dynamic_cast(m_InterpolatedSurfaceNode->GetData()); vtkSmartPointer vtkpoly = surface->GetVtkPolyData(); vtkSmartPointer vtkpoints = vtkpoly->GetPoints(); vtkSmartPointer vGrid = vtkSmartPointer::New(); vtkSmartPointer verts = vtkSmartPointer::New(); verts->GetPointIds()->SetNumberOfIds(vtkpoints->GetNumberOfPoints()); for (int i = 0; i < vtkpoints->GetNumberOfPoints(); i++) { verts->GetPointIds()->SetId(i, i); } vGrid->Allocate(1); vGrid->InsertNextCell(verts->GetCellType(), verts->GetPointIds()); vGrid->SetPoints(vtkpoints); mitk::UnstructuredGrid::Pointer interpolationGrid = mitk::UnstructuredGrid::New(); interpolationGrid->SetVtkUnstructuredGrid(vGrid); m_PointScorer->SetInput(0, uGrid); m_PointScorer->SetInput(1, interpolationGrid); m_PointScorer->Update(); mitk::UnstructuredGrid::Pointer scoredGrid = mitk::UnstructuredGrid::New(); scoredGrid = m_PointScorer->GetOutput(); mitk::ProgressBar::GetInstance()->Progress(); double spacing = mitk::SurfaceInterpolationController::GetInstance()->GetDistanceImageSpacing(); mitk::UnstructuredGridClusteringFilter::Pointer clusterFilter = mitk::UnstructuredGridClusteringFilter::New(); clusterFilter->SetInput(scoredGrid); clusterFilter->SetMeshing(false); clusterFilter->SetMinPts(4); clusterFilter->Seteps(spacing); clusterFilter->Update(); mitk::ProgressBar::GetInstance()->Progress(); // Create plane suggestion mitk::BaseRenderer::Pointer br = mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget0")); mitk::PlaneProposer planeProposer; std::vector grids = clusterFilter->GetAllClusters(); planeProposer.SetUnstructuredGrids(grids); mitk::SliceNavigationController::Pointer snc = br->GetSliceNavigationController(); planeProposer.SetSliceNavigationController(snc); planeProposer.SetUseDistances(true); try { planeProposer.CreatePlaneInfo(); } catch (const mitk::Exception &e) { MITK_ERROR << e.what(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_FirstRun = false; } void QmitkSlicesInterpolator::OnReinit3DInterpolation() { mitk::NodePredicateProperty::Pointer pred = mitk::NodePredicateProperty::New("3DContourContainer", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer contourNodes = m_DataStorage->GetDerivations(m_ToolManager->GetWorkingData(0), pred); if (contourNodes->Size() != 0) { m_BtnApply3D->setEnabled(true); m_3DContourNode = contourNodes->at(0); mitk::Surface::Pointer contours = dynamic_cast(m_3DContourNode->GetData()); if (contours) mitk::SurfaceInterpolationController::GetInstance()->ReinitializeInterpolation(contours); m_BtnReinit3DInterpolation->setEnabled(false); } else { m_BtnApply3D->setEnabled(false); QMessageBox errorInfo; errorInfo.setWindowTitle("Reinitialize surface interpolation"); errorInfo.setIcon(QMessageBox::Information); errorInfo.setText("No contours available for the selected segmentation!"); errorInfo.exec(); } } void QmitkSlicesInterpolator::OnAcceptAllPopupActivated(QAction *action) { try { std::map::const_iterator iter = ACTION_TO_SLICEDIMENSION.find(action); if (iter != ACTION_TO_SLICEDIMENSION.end()) { mitk::SliceNavigationController *slicer = iter->second; AcceptAllInterpolations(slicer); } } catch (...) { /* Showing message box with possible memory error */ QMessageBox errorInfo; errorInfo.setWindowTitle("Interpolation Process"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during interpolation. Possible cause: Not enough memory!"); errorInfo.exec(); // additional error message on std::cerr std::cerr << "Ill construction in " __FILE__ " l. " << __LINE__ << std::endl; } } void QmitkSlicesInterpolator::OnInterpolationActivated(bool on) { m_2DInterpolationEnabled = on; try { if (m_DataStorage.IsNotNull()) { if (on && !m_DataStorage->Exists(m_FeedbackNode)) { m_DataStorage->Add(m_FeedbackNode); } } } catch (...) { // don't care (double add/remove) } if (m_ToolManager) { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); mitk::DataNode *referenceNode = m_ToolManager->GetReferenceData(0); QWidget::setEnabled(workingNode != nullptr); m_BtnApply2D->setEnabled(on); m_FeedbackNode->SetVisibility(on); if (!on) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } if (workingNode) { mitk::Image *segmentation = dynamic_cast(workingNode->GetData()); if (segmentation) { m_Interpolator->SetSegmentationVolume(segmentation); if (referenceNode) { mitk::Image *referenceImage = dynamic_cast(referenceNode->GetData()); m_Interpolator->SetReferenceVolume(referenceImage); // may be nullptr } } } } UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::Run3DInterpolation() { m_SurfaceInterpolator->Interpolate(); } void QmitkSlicesInterpolator::StartUpdateInterpolationTimer() { m_Timer->start(500); } void QmitkSlicesInterpolator::StopUpdateInterpolationTimer() { m_Timer->stop(); m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB)); mitk::RenderingManager::GetInstance()->RequestUpdate( mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))->GetRenderWindow()); } void QmitkSlicesInterpolator::ChangeSurfaceColor() { float currentColor[3]; m_InterpolatedSurfaceNode->GetColor(currentColor); if (currentColor[2] == SURFACE_COLOR_RGB[2]) { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(1.0f, 1.0f, 1.0f)); } else { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(SURFACE_COLOR_RGB)); } m_InterpolatedSurfaceNode->Update(); mitk::RenderingManager::GetInstance()->RequestUpdate( mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))->GetRenderWindow()); } void QmitkSlicesInterpolator::On3DInterpolationActivated(bool on) { m_3DInterpolationEnabled = on; this->CheckSupportedImageDimension(); try { if (m_DataStorage.IsNotNull() && m_ToolManager && m_3DInterpolationEnabled) { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); if (workingNode) { if ((workingNode->IsVisible(mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))))) { int ret = QMessageBox::Yes; if (m_SurfaceInterpolator->EstimatePortionOfNeededMemory() > 0.5) { QMessageBox msgBox; msgBox.setText("Due to short handed system memory the 3D interpolation may be very slow!"); msgBox.setInformativeText("Are you sure you want to activate the 3D interpolation?"); msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); ret = msgBox.exec(); } if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); if (ret == QMessageBox::Yes) { m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } else { m_CmbInterpolation->setCurrentIndex(0); } } } else { QWidget::setEnabled(false); m_ChkShowPositionNodes->setEnabled(m_3DInterpolationEnabled); } } if (!m_3DInterpolationEnabled) { this->Show3DInterpolationResult(false); m_BtnApply3D->setEnabled(m_3DInterpolationEnabled); // T28261 // m_BtnSuggestPlane->setEnabled(m_3DInterpolationEnabled); } } catch (...) { MITK_ERROR << "Error with 3D surface interpolation!"; } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::EnableInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated OnInterpolationActivated(on); } void QmitkSlicesInterpolator::Enable3DInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated On3DInterpolationActivated(on); } void QmitkSlicesInterpolator::UpdateVisibleSuggestion() { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::OnInterpolationInfoChanged(const itk::EventObject & /*e*/) { // something (e.g. undo) changed the interpolation info, we should refresh our display UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::OnInterpolationAborted(const itk::EventObject& /*e*/) { m_CmbInterpolation->setCurrentIndex(0); m_FeedbackNode->SetData(nullptr); } void QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged(const itk::EventObject & /*e*/) { if (m_3DInterpolationEnabled) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } } void QmitkSlicesInterpolator::SetCurrentContourListID() { // New ContourList = hide current interpolation Show3DInterpolationResult(false); if (m_DataStorage.IsNotNull() && m_ToolManager && m_LastSNC) { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); if (workingNode) { QWidget::setEnabled(true); const auto timePoint = m_LastSNC->GetSelectedTimePoint(); // In case the time is not valid use 0 to access the time geometry of the working node unsigned int time_position = 0; if (!workingNode->GetData()->GetTimeGeometry()->IsValidTimePoint(timePoint)) { MITK_WARN << "Cannot accept interpolation. Time point selected by SliceNavigationController is not within the time bounds of WorkingImage. Time point: " << timePoint; return; } time_position = workingNode->GetData()->GetTimeGeometry()->TimePointToTimeStep(timePoint); mitk::Vector3D spacing = workingNode->GetData()->GetGeometry(time_position)->GetSpacing(); double minSpacing(100); double maxSpacing(0); for (int i = 0; i < 3; i++) { if (spacing[i] < minSpacing) { minSpacing = spacing[i]; } if (spacing[i] > maxSpacing) { maxSpacing = spacing[i]; } } m_SurfaceInterpolator->SetMaxSpacing(maxSpacing); m_SurfaceInterpolator->SetMinSpacing(minSpacing); m_SurfaceInterpolator->SetDistanceImageVolume(50000); mitk::Image *segmentationImage = dynamic_cast(workingNode->GetData()); m_SurfaceInterpolator->SetCurrentInterpolationSession(segmentationImage); m_SurfaceInterpolator->SetCurrentTimePoint(timePoint); if (m_3DInterpolationEnabled) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } } else { QWidget::setEnabled(false); } } } void QmitkSlicesInterpolator::Show3DInterpolationResult(bool status) { if (m_InterpolatedSurfaceNode.IsNotNull()) m_InterpolatedSurfaceNode->SetVisibility(status); if (m_3DContourNode.IsNotNull()) m_3DContourNode->SetVisibility( status, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::CheckSupportedImageDimension() { if (m_ToolManager->GetWorkingData(0)) m_Segmentation = dynamic_cast(m_ToolManager->GetWorkingData(0)->GetData()); /*if (m_3DInterpolationEnabled && m_Segmentation && m_Segmentation->GetDimension() != 3) { QMessageBox info; info.setWindowTitle("3D Interpolation Process"); info.setIcon(QMessageBox::Information); info.setText("3D Interpolation is only supported for 3D images at the moment!"); info.exec(); m_CmbInterpolation->setCurrentIndex(0); }*/ } void QmitkSlicesInterpolator::OnSliceNavigationControllerDeleted(const itk::Object *sender, const itk::EventObject & /*e*/) { // Don't know how to avoid const_cast here?! mitk::SliceNavigationController *slicer = dynamic_cast(const_cast(sender)); if (slicer) { m_ControllerToTimeObserverTag.remove(slicer); m_ControllerToSliceObserverTag.remove(slicer); m_ControllerToDeleteObserverTag.remove(slicer); } } void QmitkSlicesInterpolator::WaitForFutures() { if (m_Watcher.isRunning()) { m_Watcher.waitForFinished(); } if (m_PlaneWatcher.isRunning()) { m_PlaneWatcher.waitForFinished(); } } void QmitkSlicesInterpolator::NodeRemoved(const mitk::DataNode* node) { if ((m_ToolManager && m_ToolManager->GetWorkingData(0) == node) || node == m_3DContourNode || node == m_FeedbackNode || node == m_InterpolatedSurfaceNode) { WaitForFutures(); } } diff --git a/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp b/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp index 7589742cdf..5e1f5e62b5 100644 --- a/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp +++ b/Modules/SurfaceInterpolation/mitkComputeContourSetNormalsFilter.cpp @@ -1,343 +1,343 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkComputeContourSetNormalsFilter.h" #include "mitkIOUtil.h" #include "mitkImagePixelReadAccessor.h" mitk::ComputeContourSetNormalsFilter::ComputeContourSetNormalsFilter() : m_SegmentationBinaryImage(nullptr), m_MaxSpacing(5), m_NegativeNormalCounter(0), m_PositiveNormalCounter(0), m_UseProgressBar(false), m_ProgressStepSize(1) { mitk::Surface::Pointer output = mitk::Surface::New(); this->SetNthOutput(0, output.GetPointer()); } mitk::ComputeContourSetNormalsFilter::~ComputeContourSetNormalsFilter() { } void mitk::ComputeContourSetNormalsFilter::GenerateData() { unsigned int numberOfInputs = this->GetNumberOfIndexedInputs(); // Iterating over each input for (unsigned int i = 0; i < numberOfInputs; i++) { // Getting the inputs polydata and polygons auto *currentSurface = this->GetInput(i); vtkPolyData *polyData = currentSurface->GetVtkPolyData(); vtkSmartPointer existingPolys = polyData->GetPolys(); vtkSmartPointer existingPoints = polyData->GetPoints(); existingPolys->InitTraversal(); const vtkIdType *cell(nullptr); vtkIdType cellSize(0); // The array that contains all the vertex normals of the current polygon vtkSmartPointer normals = vtkSmartPointer::New(); normals->SetNumberOfComponents(3); normals->SetNumberOfTuples(polyData->GetNumberOfPoints()); // If the current contour is an inner contour then the direction is -1 // A contour lies inside another one if the pixel values in the direction of the normal is 1 m_NegativeNormalCounter = 0; m_PositiveNormalCounter = 0; vtkIdType offSet(0); // Iterating over each polygon for (existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);) { if (cellSize < 3) continue; // First we calculate the current polygon's normal double polygonNormal[3] = {0.0}; double p1[3]; double p2[3]; double v1[3]; double v2[3]; existingPoints->GetPoint(cell[0], p1); unsigned int index = cellSize * 0.5; existingPoints->GetPoint(cell[index], p2); v1[0] = p2[0] - p1[0]; v1[1] = p2[1] - p1[1]; v1[2] = p2[2] - p1[2]; for (vtkIdType k = 2; k < cellSize; k++) { index = cellSize * 0.25; existingPoints->GetPoint(cell[index], p1); index = cellSize * 0.75; existingPoints->GetPoint(cell[index], p2); v2[0] = p2[0] - p1[0]; v2[1] = p2[1] - p1[1]; v2[2] = p2[2] - p1[2]; vtkMath::Cross(v1, v2, polygonNormal); if (vtkMath::Norm(polygonNormal) != 0) break; } vtkMath::Normalize(polygonNormal); // Now we start computing the normal for each vertex double vertexNormalTemp[3]; existingPoints->GetPoint(cell[0], p1); existingPoints->GetPoint(cell[1], p2); v1[0] = p2[0] - p1[0]; v1[1] = p2[1] - p1[1]; v1[2] = p2[2] - p1[2]; vtkMath::Cross(v1, polygonNormal, vertexNormalTemp); vtkMath::Normalize(vertexNormalTemp); double vertexNormal[3]; for (vtkIdType j = 0; j < cellSize - 2; j++) { existingPoints->GetPoint(cell[j + 1], p1); existingPoints->GetPoint(cell[j + 2], p2); v1[0] = p2[0] - p1[0]; v1[1] = p2[1] - p1[1]; v1[2] = p2[2] - p1[2]; vtkMath::Cross(v1, polygonNormal, vertexNormal); vtkMath::Normalize(vertexNormal); double finalNormal[3]; finalNormal[0] = (vertexNormal[0] + vertexNormalTemp[0]) * 0.5; finalNormal[1] = (vertexNormal[1] + vertexNormalTemp[1]) * 0.5; finalNormal[2] = (vertexNormal[2] + vertexNormalTemp[2]) * 0.5; vtkMath::Normalize(finalNormal); // Here we determine the direction of the normal if (m_SegmentationBinaryImage) { Point3D worldCoord; worldCoord[0] = p1[0] + finalNormal[0] * m_MaxSpacing; worldCoord[1] = p1[1] + finalNormal[1] * m_MaxSpacing; worldCoord[2] = p1[2] + finalNormal[2] * m_MaxSpacing; double val = 0.0; itk::Index<3> idx; m_SegmentationBinaryImage->GetGeometry()->WorldToIndex(worldCoord, idx); try { if (m_SegmentationBinaryImage->GetImageDescriptor() ->GetChannelDescriptor() .GetPixelType() - .GetComponentType() == itk::ImageIOBase::UCHAR) + .GetComponentType() == itk::IOComponentEnum::UCHAR) { mitk::ImagePixelReadAccessor readAccess(m_SegmentationBinaryImage); val = readAccess.GetPixelByIndexSafe(idx); } else if (m_SegmentationBinaryImage->GetImageDescriptor() ->GetChannelDescriptor() .GetPixelType() - .GetComponentType() == itk::ImageIOBase::USHORT) + .GetComponentType() == itk::IOComponentEnum::USHORT) { mitk::ImagePixelReadAccessor readAccess(m_SegmentationBinaryImage); val = readAccess.GetPixelByIndexSafe(idx); } } catch (const mitk::Exception &e) { // If value is outside the image's region ignore it MITK_WARN << e.what(); } if (val == 0.0) { // MITK_INFO << "val equals zero."; ++m_PositiveNormalCounter; } else { // MITK_INFO << "val does not equal zero."; ++m_NegativeNormalCounter; } } vertexNormalTemp[0] = vertexNormal[0]; vertexNormalTemp[1] = vertexNormal[1]; vertexNormalTemp[2] = vertexNormal[2]; vtkIdType id = cell[j + 1]; normals->SetTuple(id, finalNormal); } existingPoints->GetPoint(cell[0], p1); existingPoints->GetPoint(cell[1], p2); v1[0] = p2[0] - p1[0]; v1[1] = p2[1] - p1[1]; v1[2] = p2[2] - p1[2]; vtkMath::Cross(v1, polygonNormal, vertexNormal); vtkMath::Normalize(vertexNormal); vertexNormal[0] = (vertexNormal[0] + vertexNormalTemp[0]) * 0.5; vertexNormal[1] = (vertexNormal[1] + vertexNormalTemp[1]) * 0.5; vertexNormal[2] = (vertexNormal[2] + vertexNormalTemp[2]) * 0.5; vtkMath::Normalize(vertexNormal); vtkIdType id = cell[0]; normals->SetTuple(id, vertexNormal); id = cell[cellSize - 1]; normals->SetTuple(id, vertexNormal); if (m_NegativeNormalCounter > m_PositiveNormalCounter) { for (vtkIdType n = 0; n < cellSize; n++) { double normal[3]; normals->GetTuple(offSet + n, normal); normal[0] = (-1) * normal[0]; normal[1] = (-1) * normal[1]; normal[2] = (-1) * normal[2]; normals->SetTuple(offSet + n, normal); } } m_NegativeNormalCounter = 0; m_PositiveNormalCounter = 0; offSet += cellSize; } // end for all cells Surface::Pointer surface = this->GetOutput(i); surface->GetVtkPolyData()->GetCellData()->SetNormals(normals); } // end for all inputs // Setting progressbar if (this->m_UseProgressBar) mitk::ProgressBar::GetInstance()->Progress(this->m_ProgressStepSize); } mitk::Surface::Pointer mitk::ComputeContourSetNormalsFilter::GetNormalsAsSurface() { // Just for debugging: vtkSmartPointer newPolyData = vtkSmartPointer::New(); vtkSmartPointer newLines = vtkSmartPointer::New(); vtkSmartPointer newPoints = vtkSmartPointer::New(); unsigned int idCounter(0); // Debug end for (unsigned int i = 0; i < this->GetNumberOfIndexedOutputs(); i++) { auto *currentSurface = this->GetOutput(i); vtkPolyData *polyData = currentSurface->GetVtkPolyData(); vtkSmartPointer currentCellNormals = vtkDoubleArray::SafeDownCast(polyData->GetCellData()->GetNormals()); vtkSmartPointer existingPolys = polyData->GetPolys(); vtkSmartPointer existingPoints = polyData->GetPoints(); existingPolys->InitTraversal(); const vtkIdType *cell(nullptr); vtkIdType cellSize(0); for (existingPolys->InitTraversal(); existingPolys->GetNextCell(cellSize, cell);) { for (vtkIdType j = 0; j < cellSize; j++) { double currentNormal[3]; currentCellNormals->GetTuple(cell[j], currentNormal); vtkSmartPointer line = vtkSmartPointer::New(); line->GetPointIds()->SetNumberOfIds(2); double newPoint[3]; double p0[3]; existingPoints->GetPoint(cell[j], p0); newPoint[0] = p0[0] + currentNormal[0]; newPoint[1] = p0[1] + currentNormal[1]; newPoint[2] = p0[2] + currentNormal[2]; line->GetPointIds()->SetId(0, idCounter); newPoints->InsertPoint(idCounter, p0); idCounter++; line->GetPointIds()->SetId(1, idCounter); newPoints->InsertPoint(idCounter, newPoint); idCounter++; newLines->InsertNextCell(line); } // end for all points } // end for all cells } // end for all outputs newPolyData->SetPoints(newPoints); newPolyData->SetLines(newLines); newPolyData->BuildCells(); mitk::Surface::Pointer surface = mitk::Surface::New(); surface->SetVtkPolyData(newPolyData); return surface; } void mitk::ComputeContourSetNormalsFilter::SetMaxSpacing(double maxSpacing) { m_MaxSpacing = maxSpacing; } void mitk::ComputeContourSetNormalsFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } void mitk::ComputeContourSetNormalsFilter::Reset() { for (unsigned int i = 0; i < this->GetNumberOfIndexedInputs(); i++) { this->PopBackInput(); } this->SetNumberOfIndexedInputs(0); this->SetNumberOfIndexedOutputs(0); mitk::Surface::Pointer output = mitk::Surface::New(); this->SetNthOutput(0, output.GetPointer()); } void mitk::ComputeContourSetNormalsFilter::SetUseProgressBar(bool status) { this->m_UseProgressBar = status; } void mitk::ComputeContourSetNormalsFilter::SetProgressStepSize(unsigned int stepSize) { this->m_ProgressStepSize = stepSize; } diff --git a/Modules/ToFHardware/mitkToFCameraDevice.cpp b/Modules/ToFHardware/mitkToFCameraDevice.cpp index 780f44b8f0..8e07f705a7 100644 --- a/Modules/ToFHardware/mitkToFCameraDevice.cpp +++ b/Modules/ToFHardware/mitkToFCameraDevice.cpp @@ -1,185 +1,179 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkToFCameraDevice.h" #include namespace mitk { ToFCameraDevice::ToFCameraDevice():m_BufferSize(1),m_MaxBufferSize(100),m_CurrentPos(-1),m_FreePos(0), m_CaptureWidth(204),m_CaptureHeight(204),m_PixelNumber(41616),m_SourceDataSize(0), - m_ThreadID(0),m_CameraActive(false),m_CameraConnected(false),m_ImageSequence(0) + m_CameraActive(false),m_CameraConnected(false),m_ImageSequence(0) { this->m_AmplitudeArray = nullptr; this->m_IntensityArray = nullptr; this->m_DistanceArray = nullptr; this->m_PropertyList = mitk::PropertyList::New(); //By default, all devices have no further images (just a distance image) //If a device provides more data (e.g. RGB, Intensity, Amplitde images, //the property has to be true. this->m_PropertyList->SetBoolProperty("HasRGBImage", false); this->m_PropertyList->SetBoolProperty("HasIntensityImage", false); this->m_PropertyList->SetBoolProperty("HasAmplitudeImage", false); - this->m_MultiThreader = itk::MultiThreader::New(); - this->m_ImageMutex = itk::FastMutexLock::New(); - this->m_CameraActiveMutex = itk::FastMutexLock::New(); - this->m_RGBImageWidth = this->m_CaptureWidth; this->m_RGBImageHeight = this->m_CaptureHeight; this->m_RGBPixelNumber = this->m_RGBImageWidth* this->m_RGBImageHeight; } ToFCameraDevice::~ToFCameraDevice() { } void ToFCameraDevice::SetBoolProperty( const char* propertyKey, bool boolValue ) { SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void ToFCameraDevice::SetIntProperty( const char* propertyKey, int intValue ) { SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void ToFCameraDevice::SetFloatProperty( const char* propertyKey, float floatValue ) { SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void ToFCameraDevice::SetStringProperty( const char* propertyKey, const char* stringValue ) { SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void ToFCameraDevice::SetProperty( const char *propertyKey, BaseProperty* propertyValue ) { this->m_PropertyList->SetProperty(propertyKey, propertyValue); } BaseProperty* ToFCameraDevice::GetProperty(const char *propertyKey) { return this->m_PropertyList->GetProperty(propertyKey); } bool ToFCameraDevice::GetBoolProperty(const char *propertyKey, bool& boolValue) { mitk::BoolProperty::Pointer boolprop = dynamic_cast(this->GetProperty(propertyKey)); if(boolprop.IsNull()) return false; boolValue = boolprop->GetValue(); return true; } bool ToFCameraDevice::GetStringProperty(const char *propertyKey, std::string& string) { mitk::StringProperty::Pointer stringProp = dynamic_cast(this->GetProperty(propertyKey)); if(stringProp.IsNull()) { return false; } else { string = stringProp->GetValue(); return true; } } bool ToFCameraDevice::GetIntProperty(const char *propertyKey, int& integer) { mitk::IntProperty::Pointer intProp = dynamic_cast(this->GetProperty(propertyKey)); if(intProp.IsNull()) { return false; } else { integer = intProp->GetValue(); return true; } } void ToFCameraDevice::CleanupPixelArrays() { if (m_IntensityArray) { delete [] m_IntensityArray; } if (m_DistanceArray) { delete [] m_DistanceArray; } if (m_AmplitudeArray) { delete [] m_AmplitudeArray; } } void ToFCameraDevice::AllocatePixelArrays() { // free memory if it was already allocated CleanupPixelArrays(); // allocate buffer this->m_IntensityArray = new float[this->m_PixelNumber]; for(int i=0; im_PixelNumber; i++) {this->m_IntensityArray[i]=0.0;} this->m_DistanceArray = new float[this->m_PixelNumber]; for(int i=0; im_PixelNumber; i++) {this->m_DistanceArray[i]=0.0;} this->m_AmplitudeArray = new float[this->m_PixelNumber]; for(int i=0; im_PixelNumber; i++) {this->m_AmplitudeArray[i]=0.0;} } int ToFCameraDevice::GetRGBCaptureWidth() { return this->m_RGBImageWidth; } int ToFCameraDevice::GetRGBCaptureHeight() { return this->m_RGBImageHeight; } void ToFCameraDevice::StopCamera() { - m_CameraActiveMutex->Lock(); + m_CameraActiveMutex.lock(); m_CameraActive = false; - m_CameraActiveMutex->Unlock(); + m_CameraActiveMutex.unlock(); itksys::SystemTools::Delay(100); - if (m_MultiThreader.IsNotNull()) - { - m_MultiThreader->TerminateThread(m_ThreadID); - } + if (m_Thread.joinable()) + m_Thread.detach(); // wait a little to make sure that the thread is terminated itksys::SystemTools::Delay(100); } bool ToFCameraDevice::IsCameraActive() { - m_CameraActiveMutex->Lock(); + m_CameraActiveMutex.lock(); bool ok = m_CameraActive; - m_CameraActiveMutex->Unlock(); + m_CameraActiveMutex.unlock(); return ok; } bool ToFCameraDevice::ConnectCamera() { // Prepare connection, fail if this fails. if (! this->OnConnectCamera()) return false; return true; } bool ToFCameraDevice::IsCameraConnected() { return m_CameraConnected; } } diff --git a/Modules/ToFHardware/mitkToFCameraDevice.h b/Modules/ToFHardware/mitkToFCameraDevice.h index be4f4537ad..9ec63d213c 100644 --- a/Modules/ToFHardware/mitkToFCameraDevice.h +++ b/Modules/ToFHardware/mitkToFCameraDevice.h @@ -1,231 +1,231 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkToFCameraDevice_h #define __mitkToFCameraDevice_h #include #include "mitkCommon.h" #include "mitkStringProperty.h" #include "mitkProperties.h" #include "mitkPropertyList.h" #include "itkObject.h" #include "itkObjectFactory.h" -#include "itkMultiThreader.h" -#include "itkFastMutexLock.h" + +#include +#include // Microservices #include namespace mitk { /** * @brief Virtual interface and base class for all Time-of-Flight devices. * * @ingroup ToFHardware */ class MITKTOFHARDWARE_EXPORT ToFCameraDevice : public itk::Object { public: mitkClassMacroItkParent(ToFCameraDevice, itk::Object); /*! \brief Opens a connection to the ToF camera. Has to be implemented in the specialized inherited classes. \return True for success. */ virtual bool OnConnectCamera() = 0; /** * @brief ConnectCamera Internally calls OnConnectCamera() of the * respective device implementation. * @return True for success. */ virtual bool ConnectCamera(); /*! \brief closes the connection to the camera */ virtual bool DisconnectCamera() = 0; /*! \brief starts the continuous updating of the camera. A separate thread updates the source data, the main thread processes the source data and creates images and coordinates */ virtual void StartCamera() = 0; /*! \brief stops the continuous updating of the camera */ virtual void StopCamera(); /*! \brief returns true if the camera is connected and started */ virtual bool IsCameraActive(); /*! \brief returns true if the camera is connected */ virtual bool IsCameraConnected(); /*! \brief updates the camera for image acquisition */ virtual void UpdateCamera() = 0; /*! \brief gets the amplitude data from the ToF camera as the strength of the active illumination of every pixel These values can be used to determine the quality of the distance values. The higher the amplitude value, the higher the accuracy of the according distance value \param imageSequence the actually captured image sequence number \param amplitudeArray contains the returned amplitude data as an array. */ virtual void GetAmplitudes(float* amplitudeArray, int& imageSequence) = 0; /*! \brief gets the intensity data from the ToF camera as a greyscale image \param intensityArray contains the returned intensities data as an array. \param imageSequence the actually captured image sequence number */ virtual void GetIntensities(float* intensityArray, int& imageSequence) = 0; /*! \brief gets the distance data from the ToF camera measuring the distance between the camera and the different object points in millimeters \param distanceArray contains the returned distances data as an array. \param imageSequence the actually captured image sequence number */ virtual void GetDistances(float* distanceArray, int& imageSequence) = 0; /*! \brief gets the 3 images (distance, amplitude, intensity) from the ToF camera. Caution! The user is responsible for allocating and deleting the images. \param distanceArray contains the returned distance data as an array. \param amplitudeArray contains the returned amplitude data as an array. \param intensityArray contains the returned intensity data as an array. \param sourceDataArray contains the complete source data from the camera device. \param requiredImageSequence the required image sequence number \param capturedImageSequence the actually captured image sequence number \param rgbDataArray */ virtual void GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* sourceDataArray, int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray=nullptr) = 0; /*! \brief get the currently set capture width \return capture width */ itkGetMacro(CaptureWidth, int); /*! \brief get the currently set capture height \return capture height */ itkGetMacro(CaptureHeight, int); /*! \brief get the currently set source data size \return source data size */ itkGetMacro(SourceDataSize, int); /*! \brief get the currently set buffer size \return buffer size */ itkGetMacro(BufferSize, int); /*! \brief get the currently set max buffer size \return max buffer size */ itkGetMacro(MaxBufferSize, int); /*! \brief set a bool property in the property list */ void SetBoolProperty( const char* propertyKey, bool boolValue ); /*! \brief set an int property in the property list */ void SetIntProperty( const char* propertyKey, int intValue ); /*! \brief set a float property in the property list */ void SetFloatProperty( const char* propertyKey, float floatValue ); /*! \brief set a string property in the property list */ void SetStringProperty( const char* propertyKey, const char* stringValue ); /*! \brief set a BaseProperty property in the property list */ virtual void SetProperty( const char *propertyKey, BaseProperty* propertyValue ); /*! \brief get a BaseProperty from the property list */ virtual BaseProperty* GetProperty( const char *propertyKey ); /*! \brief get a bool from the property list */ bool GetBoolProperty(const char *propertyKey, bool& boolValue); /*! \brief get a string from the property list */ bool GetStringProperty(const char *propertyKey, std::string& string); /*! \brief get an int from the property list */ bool GetIntProperty(const char *propertyKey, int& integer); virtual int GetRGBCaptureWidth(); virtual int GetRGBCaptureHeight(); protected: ToFCameraDevice(); ~ToFCameraDevice() override; /*! \brief method for allocating memory for pixel arrays m_IntensityArray, m_DistanceArray and m_AmplitudeArray */ virtual void AllocatePixelArrays(); /*! \brief method for cleanup memory allocated for pixel arrays m_IntensityArray, m_DistanceArray and m_AmplitudeArray */ virtual void CleanupPixelArrays(); float* m_IntensityArray; ///< float array holding the intensity image float* m_DistanceArray; ///< float array holding the distance image float* m_AmplitudeArray; ///< float array holding the amplitude image int m_BufferSize; ///< buffer size of the image buffer needed for loss-less acquisition of range data int m_MaxBufferSize; ///< maximal buffer size needed for initialization of data arrays. Default value is 100. int m_CurrentPos; ///< current position in the buffer which will be retrieved by the Get methods int m_FreePos; ///< current position in the buffer which will be filled with data acquired from the hardware int m_CaptureWidth; ///< width of the range image (x dimension) int m_CaptureHeight; ///< height of the range image (y dimension) int m_PixelNumber; ///< number of pixels in the range image (m_CaptureWidth*m_CaptureHeight) int m_RGBImageWidth; ///< width of the RGB image (x dimension) int m_RGBImageHeight; ///< height of the RGB image (y dimension) int m_RGBPixelNumber; ///< number of pixels in the range image (m_RGBImageWidth*m_RGBImageHeight) int m_SourceDataSize; ///< size of the PMD source data - itk::MultiThreader::Pointer m_MultiThreader; ///< itk::MultiThreader used for thread handling - itk::FastMutexLock::Pointer m_ImageMutex; ///< mutex for images provided by the range camera - itk::FastMutexLock::Pointer m_CameraActiveMutex; ///< mutex for the cameraActive flag - int m_ThreadID; ///< ID of the started thread + std::mutex m_ImageMutex; ///< mutex for images provided by the range camera + std::mutex m_CameraActiveMutex; ///< mutex for the cameraActive flag + std::thread m_Thread; bool m_CameraActive; ///< flag indicating if the camera is currently active or not. Caution: thread safe access only! bool m_CameraConnected; ///< flag indicating if the camera is successfully connected or not. Caution: thread safe access only! int m_ImageSequence; ///< counter for acquired images PropertyList::Pointer m_PropertyList; ///< a list of the corresponding properties }; } //END mitk namespace /** ToFCameraDevice is declared a MicroService interface. See MicroService documenation for more details. */ MITK_DECLARE_SERVICE_INTERFACE(mitk::ToFCameraDevice, "org.mitk.services.ToFCameraDevice") #endif diff --git a/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.cpp b/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.cpp index 1ea0743577..92372f5476 100644 --- a/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.cpp +++ b/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.cpp @@ -1,378 +1,362 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkToFCameraMITKPlayerDevice.h" #include "mitkToFCameraMITKPlayerController.h" #include "mitkRealTimeClock.h" #include #include -#include #include namespace mitk { ToFCameraMITKPlayerDevice::ToFCameraMITKPlayerDevice() : m_DistanceDataBuffer(nullptr), m_AmplitudeDataBuffer(nullptr), m_IntensityDataBuffer(nullptr), m_RGBDataBuffer(nullptr) { m_Controller = ToFCameraMITKPlayerController::New(); } ToFCameraMITKPlayerDevice::~ToFCameraMITKPlayerDevice() { DisconnectCamera(); CleanUpDataBuffers(); } bool ToFCameraMITKPlayerDevice::OnConnectCamera() { bool ok = m_Controller->OpenCameraConnection(); if (ok) { this->m_CaptureWidth = m_Controller->GetCaptureWidth(); this->m_CaptureHeight = m_Controller->GetCaptureHeight(); this->m_RGBImageWidth = m_Controller->GetRGBCaptureWidth(); this->m_RGBImageHeight = m_Controller->GetRGBCaptureHeight(); this->m_PixelNumber = m_Controller->GetPixelNumber(); this->m_RGBPixelNumber = m_Controller->GetRGBPixelNumber(); if(m_RGBPixelNumber != m_PixelNumber) this->SetBoolProperty("RGBImageHasDifferentResolution", true); AllocatePixelArrays(); AllocateDataBuffers(); m_CameraConnected = true; } return ok; } bool ToFCameraMITKPlayerDevice::DisconnectCamera() { bool ok = m_Controller->CloseCameraConnection(); if (ok) { m_CameraConnected = false; m_PropertyList->Clear(); } return ok; } void ToFCameraMITKPlayerDevice::StartCamera() { if (m_CameraConnected) { // get the first image this->m_Controller->UpdateCamera(); - this->m_ImageMutex->Lock(); + this->m_ImageMutex.lock(); this->m_Controller->GetDistances(this->m_DistanceDataBuffer[this->m_FreePos]); this->m_Controller->GetAmplitudes(this->m_AmplitudeDataBuffer[this->m_FreePos]); this->m_Controller->GetIntensities(this->m_IntensityDataBuffer[this->m_FreePos]); this->m_Controller->GetRgb(this->m_RGBDataBuffer[this->m_FreePos]); this->m_FreePos = (this->m_FreePos+1) % this->m_BufferSize; this->m_CurrentPos = (this->m_CurrentPos+1) % this->m_BufferSize; this->m_ImageSequence++; - this->m_ImageMutex->Unlock(); + this->m_ImageMutex.unlock(); - this->m_CameraActiveMutex->Lock(); + this->m_CameraActiveMutex.lock(); this->m_CameraActive = true; - this->m_CameraActiveMutex->Unlock(); - this->m_ThreadID = this->m_MultiThreader->SpawnThread(this->Acquire, this); + this->m_CameraActiveMutex.unlock(); + m_Thread = std::thread(&ToFCameraMITKPlayerDevice::Acquire, this); // wait a little to make sure that the thread is started itksys::SystemTools::Delay(10); } else { MITK_INFO<<"Camera not connected"; } } void ToFCameraMITKPlayerDevice::UpdateCamera() { m_Controller->UpdateCamera(); } -ITK_THREAD_RETURN_TYPE ToFCameraMITKPlayerDevice::Acquire(void* pInfoStruct) +void ToFCameraMITKPlayerDevice::Acquire() { - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - if (pInfo == nullptr) + mitk::RealTimeClock::Pointer realTimeClock; + realTimeClock = mitk::RealTimeClock::New(); + int n = 100; + double t1, t2; + t1 = realTimeClock->GetCurrentStamp(); + bool overflow = false; + bool printStatus = false; + while (this->IsCameraActive()) { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - ToFCameraMITKPlayerDevice* toFCameraDevice = (ToFCameraMITKPlayerDevice*)pInfo->UserData; - if (toFCameraDevice!=nullptr) - { - mitk::RealTimeClock::Pointer realTimeClock; - realTimeClock = mitk::RealTimeClock::New(); - int n = 100; - double t1, t2; - t1 = realTimeClock->GetCurrentStamp(); - bool overflow = false; - bool printStatus = false; - while (toFCameraDevice->IsCameraActive()) + // update the ToF camera + this->UpdateCamera(); + // get image data from controller and write it to the according buffer + m_Controller->GetDistances(m_DistanceDataBuffer[m_FreePos]); + m_Controller->GetAmplitudes(m_AmplitudeDataBuffer[m_FreePos]); + m_Controller->GetIntensities(m_IntensityDataBuffer[m_FreePos]); + m_Controller->GetRgb(m_RGBDataBuffer[m_FreePos]); + this->Modified(); + m_ImageMutex.lock(); + m_FreePos = (m_FreePos+1) % m_BufferSize; + m_CurrentPos = (m_CurrentPos+1) % m_BufferSize; + m_ImageSequence++; + if (m_FreePos == m_CurrentPos) { - // update the ToF camera - toFCameraDevice->UpdateCamera(); - // get image data from controller and write it to the according buffer - toFCameraDevice->m_Controller->GetDistances(toFCameraDevice->m_DistanceDataBuffer[toFCameraDevice->m_FreePos]); - toFCameraDevice->m_Controller->GetAmplitudes(toFCameraDevice->m_AmplitudeDataBuffer[toFCameraDevice->m_FreePos]); - toFCameraDevice->m_Controller->GetIntensities(toFCameraDevice->m_IntensityDataBuffer[toFCameraDevice->m_FreePos]); - toFCameraDevice->m_Controller->GetRgb(toFCameraDevice->m_RGBDataBuffer[toFCameraDevice->m_FreePos]); - toFCameraDevice->Modified(); - toFCameraDevice->m_ImageMutex->Lock(); - toFCameraDevice->m_FreePos = (toFCameraDevice->m_FreePos+1) % toFCameraDevice->m_BufferSize; - toFCameraDevice->m_CurrentPos = (toFCameraDevice->m_CurrentPos+1) % toFCameraDevice->m_BufferSize; - toFCameraDevice->m_ImageSequence++; - if (toFCameraDevice->m_FreePos == toFCameraDevice->m_CurrentPos) - { - overflow = true; - } - if (toFCameraDevice->m_ImageSequence % n == 0) - { - printStatus = true; - } - toFCameraDevice->m_ImageMutex->Unlock(); - if (overflow) - { - overflow = false; - } - // print current framerate - if (printStatus) - { - t2 = realTimeClock->GetCurrentStamp() - t1; - MITK_INFO << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << toFCameraDevice->m_ImageSequence; - t1 = realTimeClock->GetCurrentStamp(); - printStatus = false; - } - } // end of while loop - } - return ITK_THREAD_RETURN_VALUE; + overflow = true; + } + if (m_ImageSequence % n == 0) + { + printStatus = true; + } + m_ImageMutex.unlock(); + if (overflow) + { + overflow = false; + } + // print current framerate + if (printStatus) + { + t2 = realTimeClock->GetCurrentStamp() - t1; + MITK_INFO << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << m_ImageSequence; + t1 = realTimeClock->GetCurrentStamp(); + printStatus = false; + } + } // end of while loop } void ToFCameraMITKPlayerDevice::GetAmplitudes(float* amplitudeArray, int& imageSequence) { - m_ImageMutex->Lock(); + m_ImageMutex.lock(); // write amplitude image data to float array for (int i=0; im_PixelNumber; i++) { amplitudeArray[i] = this->m_AmplitudeDataBuffer[this->m_CurrentPos][i]; } imageSequence = this->m_ImageSequence; - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); } void ToFCameraMITKPlayerDevice::GetIntensities(float* intensityArray, int& imageSequence) { - m_ImageMutex->Lock(); + m_ImageMutex.lock(); // write intensity image data to float array for (int i=0; im_PixelNumber; i++) { intensityArray[i] = this->m_IntensityDataBuffer[this->m_CurrentPos][i]; } imageSequence = this->m_ImageSequence; - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); } void ToFCameraMITKPlayerDevice::GetDistances(float* distanceArray, int& imageSequence) { - m_ImageMutex->Lock(); + m_ImageMutex.lock(); // write distance image data to float array for (int i=0; im_PixelNumber; i++) { distanceArray[i] = this->m_DistanceDataBuffer[this->m_CurrentPos][i]; } imageSequence = this->m_ImageSequence; - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); } void ToFCameraMITKPlayerDevice::GetRgb(unsigned char* rgbArray, int& imageSequence) { - m_ImageMutex->Lock(); + m_ImageMutex.lock(); // write intensity image data to unsigned char array for (int i=0; im_RGBPixelNumber*3; i++) { rgbArray[i] = this->m_RGBDataBuffer[this->m_CurrentPos][i]; } imageSequence = this->m_ImageSequence; - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); } void ToFCameraMITKPlayerDevice::GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* /*sourceDataArray*/, int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray) { - m_ImageMutex->Lock(); + m_ImageMutex.lock(); //check for empty buffer if (this->m_ImageSequence < 0) { // buffer empty MITK_INFO << "Buffer empty!! "; capturedImageSequence = this->m_ImageSequence; - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); return; } // determine position of image in buffer int pos = 0; if ((requiredImageSequence < 0) || (requiredImageSequence > this->m_ImageSequence)) { capturedImageSequence = this->m_ImageSequence; pos = this->m_CurrentPos; } else if (requiredImageSequence <= this->m_ImageSequence - this->m_BufferSize) { capturedImageSequence = (this->m_ImageSequence - this->m_BufferSize) + 1; pos = (this->m_CurrentPos + 1) % this->m_BufferSize; } else // (requiredImageSequence > this->m_ImageSequence - this->m_BufferSize) && (requiredImageSequence <= this->m_ImageSequence) { capturedImageSequence = requiredImageSequence; pos = (this->m_CurrentPos + (10-(this->m_ImageSequence - requiredImageSequence))) % this->m_BufferSize; } if(this->m_DistanceDataBuffer&&this->m_AmplitudeDataBuffer&&this->m_IntensityDataBuffer&&this->m_RGBDataBuffer) { // write image data to float arrays memcpy(distanceArray, this->m_DistanceDataBuffer[pos], this->m_PixelNumber * sizeof(float)); memcpy(amplitudeArray, this->m_AmplitudeDataBuffer[pos], this->m_PixelNumber * sizeof(float)); memcpy(intensityArray, this->m_IntensityDataBuffer[pos], this->m_PixelNumber * sizeof(float)); memcpy(rgbDataArray, this->m_RGBDataBuffer[pos], this->m_RGBPixelNumber * 3 * sizeof(unsigned char)); } - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); } void ToFCameraMITKPlayerDevice::SetInputFileName(std::string inputFileName) { this->m_InputFileName = inputFileName; this->m_Controller->SetInputFileName(inputFileName); } void ToFCameraMITKPlayerDevice::SetProperty( const char *propertyKey, BaseProperty* propertyValue ) { this->m_PropertyList->SetProperty(propertyKey, propertyValue); ToFCameraMITKPlayerController::Pointer myController = dynamic_cast(this->m_Controller.GetPointer()); std::string strValue; GetStringProperty(propertyKey, strValue); if (strcmp(propertyKey, "DistanceImageFileName") == 0) { myController->SetDistanceImageFileName(strValue); } else if (strcmp(propertyKey, "AmplitudeImageFileName") == 0) { std::ifstream amplitudeImage(strValue.c_str()); if(amplitudeImage) { this->m_PropertyList->SetBoolProperty("HasAmplitudeImage", true); myController->SetAmplitudeImageFileName(strValue); } else { MITK_WARN << "File " << strValue << " does not exist!"; } } else if (strcmp(propertyKey, "IntensityImageFileName") == 0) { std::ifstream intensityImage(strValue.c_str()); if(intensityImage) { this->m_PropertyList->SetBoolProperty("HasIntensityImage", true); myController->SetIntensityImageFileName(strValue); } else { MITK_WARN << "File " << strValue << " does not exist!"; } } else if (strcmp(propertyKey, "RGBImageFileName") == 0) { std::ifstream intensityImage(strValue.c_str()); if(intensityImage) { this->m_PropertyList->SetBoolProperty("HasRGBImage", true); myController->SetRGBImageFileName(strValue); } else { MITK_WARN << "File " << strValue << " does not exist!"; } } } void ToFCameraMITKPlayerDevice::CleanUpDataBuffers() { if (m_DistanceDataBuffer) { for(int i=0; im_MaxBufferSize; i++) { delete[] this->m_DistanceDataBuffer[i]; } delete[] this->m_DistanceDataBuffer; } if (m_AmplitudeDataBuffer) { for(int i=0; im_MaxBufferSize; i++) { delete[] this->m_AmplitudeDataBuffer[i]; } delete[] this->m_AmplitudeDataBuffer; } if (m_IntensityDataBuffer) { for(int i=0; im_MaxBufferSize; i++) { delete[] this->m_IntensityDataBuffer[i]; } delete[] this->m_IntensityDataBuffer; } if (m_RGBDataBuffer) { for(int i=0; im_MaxBufferSize; i++) { delete[] this->m_RGBDataBuffer[i]; } delete[] this->m_RGBDataBuffer; } } void ToFCameraMITKPlayerDevice::AllocateDataBuffers() { // free memory if it was already allocated this->CleanUpDataBuffers(); // allocate buffers this->m_DistanceDataBuffer = new float*[this->m_MaxBufferSize]; for(int i=0; im_MaxBufferSize; i++) { this->m_DistanceDataBuffer[i] = new float[this->m_PixelNumber]; } this->m_AmplitudeDataBuffer = new float*[this->m_MaxBufferSize]; for(int i=0; im_MaxBufferSize; i++) { this->m_AmplitudeDataBuffer[i] = new float[this->m_PixelNumber]; } this->m_IntensityDataBuffer = new float*[this->m_MaxBufferSize]; for(int i=0; im_MaxBufferSize; i++) { this->m_IntensityDataBuffer[i] = new float[this->m_PixelNumber]; } this->m_RGBDataBuffer = new unsigned char*[this->m_MaxBufferSize]; for(int i=0; im_MaxBufferSize; i++) { this->m_RGBDataBuffer[i] = new unsigned char[this->m_RGBPixelNumber*3]; } } } diff --git a/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.h b/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.h index 52662df0c4..275265139a 100644 --- a/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.h +++ b/Modules/ToFHardware/mitkToFCameraMITKPlayerDevice.h @@ -1,138 +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 __mitkToFCameraMITKPlayerDevice_h #define __mitkToFCameraMITKPlayerDevice_h #include #include "mitkCommon.h" #include "mitkToFCameraDevice.h" #include "mitkToFCameraMITKPlayerController.h" #include "itkObject.h" #include "itkObjectFactory.h" -#include "itkMultiThreader.h" -#include "itkFastMutexLock.h" namespace mitk { /** * @brief Device class representing a player for MITK-ToF images. * * @ingroup ToFHardware */ class MITKTOFHARDWARE_EXPORT ToFCameraMITKPlayerDevice : public ToFCameraDevice { public: mitkClassMacro( ToFCameraMITKPlayerDevice , ToFCameraDevice ); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /*! \brief opens a connection to the ToF camera */ bool OnConnectCamera() override; /*! \brief closes the connection to the camera */ bool DisconnectCamera() override; /*! \brief starts the continuous updating of the camera. A separate thread updates the source data, the main thread processes the source data and creates images and coordinates */ void StartCamera() override; /*! \brief gets the amplitude data from the ToF camera as the strength of the active illumination of every pixel. Caution! The user is responsible for allocating and deleting the images. These values can be used to determine the quality of the distance values. The higher the amplitude value, the higher the accuracy of the according distance value \param imageSequence the actually captured image sequence number \param amplitudeArray contains the returned amplitude data as an array. */ void GetAmplitudes(float* amplitudeArray, int& imageSequence) override; /*! \brief gets the intensity data from the ToF camera as a greyscale image. Caution! The user is responsible for allocating and deleting the images. \param intensityArray contains the returned intensities data as an array. \param imageSequence the actually captured image sequence number */ void GetIntensities(float* intensityArray, int& imageSequence) override; /*! \brief gets the rgb data from the ToF camera. Caution! The user is responsible for allocating and deleting the images. \param rgbArray contains the returned rgb data as an array. \param imageSequence the actually captured image sequence number */ virtual void GetRgb(unsigned char* rgbArray, int& imageSequence); /*! \brief gets the distance data from the ToF camera measuring the distance between the camera and the different object points in millimeters. Caution! The user is responsible for allocating and deleting the images. \param distanceArray contains the returned distances data as an array. \param imageSequence the actually captured image sequence number */ void GetDistances(float* distanceArray, int& imageSequence) override; /*! \brief gets the 3 images (distance, amplitude, intensity) from the ToF camera. Caution! The user is responsible for allocating and deleting the images. \param distanceArray contains the returned distance data as an array. \param amplitudeArray contains the returned amplitude data as an array. \param intensityArray contains the returned intensity data as an array. \param sourceDataArray contains the complete source data from the camera device. \param requiredImageSequence the required image sequence number \param capturedImageSequence the actually captured image sequence number \param rgbDataArray */ void GetAllImages(float* distanceArray, float* amplitudeArray, float* intensityArray, char* sourceDataArray, int requiredImageSequence, int& capturedImageSequence, unsigned char* rgbDataArray=nullptr) override; /*! \brief Set file name where the data is recorded \param inputFileName name of input file which should be played */ virtual void SetInputFileName(std::string inputFileName); /*! \brief set a BaseProperty */ void SetProperty( const char *propertyKey, BaseProperty* propertyValue ) override; protected: ToFCameraMITKPlayerDevice(); ~ToFCameraMITKPlayerDevice() override; /*! \brief updates the camera for image acquisition */ void UpdateCamera() override; /*! \brief Thread method continuously acquiring images from the specified input file */ - static ITK_THREAD_RETURN_TYPE Acquire(void* pInfoStruct); + void Acquire(); /*! \brief Clean up memory (pixel buffers) */ void CleanUpDataBuffers(); /*! \brief Allocate pixel buffers */ void AllocateDataBuffers(); ToFCameraMITKPlayerController::Pointer m_Controller; ///< member holding the corresponding controller std::string m_InputFileName; ///< member holding the file name of the current input file private: float** m_DistanceDataBuffer; ///< buffer holding the last distance images float** m_AmplitudeDataBuffer; ///< buffer holding the last amplitude images float** m_IntensityDataBuffer; ///< buffer holding the last intensity images unsigned char** m_RGBDataBuffer; ///< buffer holding the last rgb images }; } //END mitk namespace #endif diff --git a/Modules/ToFHardware/mitkToFImageRecorder.cpp b/Modules/ToFHardware/mitkToFImageRecorder.cpp index f037ea1742..dc26e631d3 100644 --- a/Modules/ToFHardware/mitkToFImageRecorder.cpp +++ b/Modules/ToFHardware/mitkToFImageRecorder.cpp @@ -1,262 +1,243 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkToFImageRecorder.h" #include -#include #include #pragma GCC visibility push(default) #include #pragma GCC visibility pop namespace mitk { ToFImageRecorder::ToFImageRecorder() { this->m_ToFCameraDevice = nullptr; - this->m_MultiThreader = itk::MultiThreader::New(); - this->m_AbortMutex = itk::FastMutexLock::New(); - this->m_ThreadID = 0; this->m_NumOfFrames = 1; //lets record one frame per default this->m_ToFImageWriter = nullptr; this->m_DistanceImageSelected = true; //lets assume a device only has depth data by default this->m_AmplitudeImageSelected = false; this->m_IntensityImageSelected = false; this->m_RGBImageSelected = false; this->m_Abort = false; this->m_ToFCaptureWidth = 0; this->m_ToFCaptureHeight = 0; this->m_RGBCaptureWidth = 0; this->m_RGBCaptureHeight = 0; this->m_FileFormat = ".nrrd"; //lets make nrrd the default this->m_ToFPixelNumber = 0; this->m_RGBPixelNumber = 0; this->m_SourceDataSize = 0; this->m_ToFImageType = ToFImageWriter::ToFImageType3D; this->m_RecordMode = ToFImageRecorder::PerFrames; this->m_DistanceImageFileName = ""; this->m_AmplitudeImageFileName = ""; this->m_IntensityImageFileName = ""; this->m_RGBImageFileName = ""; this->m_ImageSequence = 0; this->m_DistanceArray = nullptr; this->m_AmplitudeArray = nullptr; this->m_IntensityArray = nullptr; this->m_RGBArray = nullptr; this->m_SourceDataArray = nullptr; } ToFImageRecorder::~ToFImageRecorder() { delete[] m_DistanceArray; delete[] m_AmplitudeArray; delete[] m_IntensityArray; delete[] m_RGBArray; delete[] m_SourceDataArray; } void ToFImageRecorder::StopRecording() { - this->m_AbortMutex->Lock(); + this->m_AbortMutex.lock(); this->m_Abort = true; - this->m_AbortMutex->Unlock(); + this->m_AbortMutex.unlock(); } void ToFImageRecorder::StartRecording() { if (this->m_ToFCameraDevice.IsNull()) { throw std::invalid_argument("ToFCameraDevice is nullptr."); return; } if (this->m_FileFormat.compare(".csv") == 0) { this->m_ToFImageWriter = ToFImageCsvWriter::New(); } else if(this->m_FileFormat.compare(".nrrd") == 0) { this->m_ToFImageWriter = ToFNrrdImageWriter::New(); this->m_ToFImageWriter->SetExtension(m_FileFormat); } else { throw std::logic_error("No file format specified!"); } this->m_RGBCaptureWidth = this->m_ToFCameraDevice->GetRGBCaptureWidth(); this->m_RGBCaptureHeight = this->m_ToFCameraDevice->GetRGBCaptureHeight(); this->m_RGBPixelNumber = this->m_RGBCaptureWidth * this->m_RGBCaptureHeight; this->m_ToFCaptureWidth = this->m_ToFCameraDevice->GetCaptureWidth(); this->m_ToFCaptureHeight = this->m_ToFCameraDevice->GetCaptureHeight(); this->m_ToFPixelNumber = this->m_ToFCaptureWidth * this->m_ToFCaptureHeight; this->m_SourceDataSize = this->m_ToFCameraDevice->GetSourceDataSize(); // allocate buffer if(m_IntensityArray == nullptr) { this->m_IntensityArray = new float[m_ToFPixelNumber]; } if(this->m_DistanceArray == nullptr) { this->m_DistanceArray = new float[m_ToFPixelNumber]; } if(this->m_AmplitudeArray == nullptr) { this->m_AmplitudeArray = new float[m_ToFPixelNumber]; } if(this->m_RGBArray == nullptr) { this->m_RGBArray = new unsigned char[m_RGBPixelNumber*3]; } if(this->m_SourceDataArray == nullptr) { this->m_SourceDataArray = new char[m_SourceDataSize]; } this->m_ToFImageWriter->SetDistanceImageFileName(this->m_DistanceImageFileName); this->m_ToFImageWriter->SetAmplitudeImageFileName(this->m_AmplitudeImageFileName); this->m_ToFImageWriter->SetIntensityImageFileName(this->m_IntensityImageFileName); this->m_ToFImageWriter->SetRGBImageFileName(this->m_RGBImageFileName); this->m_ToFImageWriter->SetRGBCaptureWidth(this->m_RGBCaptureWidth); this->m_ToFImageWriter->SetRGBCaptureHeight(this->m_RGBCaptureHeight); this->m_ToFImageWriter->SetToFCaptureWidth(this->m_ToFCaptureWidth); this->m_ToFImageWriter->SetToFCaptureHeight(this->m_ToFCaptureHeight); this->m_ToFImageWriter->SetToFImageType(this->m_ToFImageType); this->m_ToFImageWriter->SetDistanceImageSelected(this->m_DistanceImageSelected); this->m_ToFImageWriter->SetAmplitudeImageSelected(this->m_AmplitudeImageSelected); this->m_ToFImageWriter->SetIntensityImageSelected(this->m_IntensityImageSelected); this->m_ToFImageWriter->SetRGBImageSelected(this->m_RGBImageSelected); this->m_ToFImageWriter->Open(); - this->m_AbortMutex->Lock(); + this->m_AbortMutex.lock(); this->m_Abort = false; - this->m_AbortMutex->Unlock(); - this->m_ThreadID = this->m_MultiThreader->SpawnThread(this->RecordData, this); + this->m_AbortMutex.unlock(); + m_Thread = std::thread(&ToFImageRecorder::RecordData, this); } void ToFImageRecorder::WaitForThreadBeingTerminated() { - this->m_MultiThreader->TerminateThread(this->m_ThreadID); + if (m_Thread.joinable()) + m_Thread.join(); } -ITK_THREAD_RETURN_TYPE ToFImageRecorder::RecordData(void* pInfoStruct) +void ToFImageRecorder::RecordData() { - struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - if (pInfo == nullptr) + ToFCameraDevice::Pointer toFCameraDevice = this->GetCameraDevice(); + + mitk::RealTimeClock::Pointer realTimeClock; + realTimeClock = mitk::RealTimeClock::New(); + int n = 100; + double t1 = 0; + double t2 = 0; + t1 = realTimeClock->GetCurrentStamp(); + bool printStatus = false; + int requiredImageSequence = 0; + int numOfFramesRecorded = 0; + + bool abort = false; + m_AbortMutex.lock(); + abort = m_Abort; + m_AbortMutex.unlock(); + while ( !abort ) { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - ToFImageRecorder* toFImageRecorder = (ToFImageRecorder*)pInfo->UserData; - if (toFImageRecorder!=nullptr) - { - - ToFCameraDevice::Pointer toFCameraDevice = toFImageRecorder->GetCameraDevice(); - - mitk::RealTimeClock::Pointer realTimeClock; - realTimeClock = mitk::RealTimeClock::New(); - int n = 100; - double t1 = 0; - double t2 = 0; - t1 = realTimeClock->GetCurrentStamp(); - bool printStatus = false; - int requiredImageSequence = 0; - int numOfFramesRecorded = 0; - - bool abort = false; - toFImageRecorder->m_AbortMutex->Lock(); - abort = toFImageRecorder->m_Abort; - toFImageRecorder->m_AbortMutex->Unlock(); - while ( !abort ) + if ( ((m_RecordMode == ToFImageRecorder::PerFrames) && (numOfFramesRecorded < m_NumOfFrames)) || + (m_RecordMode == ToFImageRecorder::Infinite) ) { - if ( ((toFImageRecorder->m_RecordMode == ToFImageRecorder::PerFrames) && (numOfFramesRecorded < toFImageRecorder->m_NumOfFrames)) || - (toFImageRecorder->m_RecordMode == ToFImageRecorder::Infinite) ) - { - toFCameraDevice->GetAllImages(toFImageRecorder->m_DistanceArray, toFImageRecorder->m_AmplitudeArray, - toFImageRecorder->m_IntensityArray, toFImageRecorder->m_SourceDataArray, requiredImageSequence, toFImageRecorder->m_ImageSequence, toFImageRecorder->m_RGBArray ); + toFCameraDevice->GetAllImages(m_DistanceArray, m_AmplitudeArray, + m_IntensityArray, m_SourceDataArray, requiredImageSequence, m_ImageSequence, m_RGBArray ); - if (toFImageRecorder->m_ImageSequence >= requiredImageSequence) + if (m_ImageSequence >= requiredImageSequence) + { + if (m_ImageSequence > requiredImageSequence) { - if (toFImageRecorder->m_ImageSequence > requiredImageSequence) - { - MITK_INFO << "Problem! required: " << requiredImageSequence << " captured: " << toFImageRecorder->m_ImageSequence; - } - requiredImageSequence = toFImageRecorder->m_ImageSequence + 1; - toFImageRecorder->m_ToFImageWriter->Add( toFImageRecorder->m_DistanceArray, - toFImageRecorder->m_AmplitudeArray, toFImageRecorder->m_IntensityArray, toFImageRecorder->m_RGBArray ); - numOfFramesRecorded++; - if (numOfFramesRecorded % n == 0) - { - printStatus = true; - } - if (printStatus) - { - t2 = realTimeClock->GetCurrentStamp() - t1; - MITK_INFO << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << toFImageRecorder->m_ImageSequence; - t1 = realTimeClock->GetCurrentStamp(); - printStatus = false; - } + MITK_INFO << "Problem! required: " << requiredImageSequence << " captured: " << m_ImageSequence; + } + requiredImageSequence = m_ImageSequence + 1; + m_ToFImageWriter->Add(m_DistanceArray, m_AmplitudeArray, m_IntensityArray, m_RGBArray ); + numOfFramesRecorded++; + if (numOfFramesRecorded % n == 0) + { + printStatus = true; + } + if (printStatus) + { + t2 = realTimeClock->GetCurrentStamp() - t1; + MITK_INFO << " Framerate (fps): " << n / (t2/1000) << " Sequence: " << m_ImageSequence; + t1 = realTimeClock->GetCurrentStamp(); + printStatus = false; } - toFImageRecorder->m_AbortMutex->Lock(); - abort = toFImageRecorder->m_Abort; - toFImageRecorder->m_AbortMutex->Unlock(); - } - else - { - abort = true; } - } // end of while loop + m_AbortMutex.lock(); + abort = m_Abort; + m_AbortMutex.unlock(); + } + else + { + abort = true; + } + } // end of while loop - toFImageRecorder->InvokeEvent(itk::AbortEvent()); + this->InvokeEvent(itk::AbortEvent()); - toFImageRecorder->m_ToFImageWriter->Close(); - } - return ITK_THREAD_RETURN_VALUE; + m_ToFImageWriter->Close(); } void ToFImageRecorder::SetCameraDevice(ToFCameraDevice* aToFCameraDevice) { this->m_ToFCameraDevice = aToFCameraDevice; } ToFCameraDevice* ToFImageRecorder::GetCameraDevice() { return this->m_ToFCameraDevice; } ToFImageWriter::ToFImageType ToFImageRecorder::GetToFImageType() { return this->m_ToFImageType; } void ToFImageRecorder::SetToFImageType(ToFImageWriter::ToFImageType toFImageType) { this->m_ToFImageType = toFImageType; } ToFImageRecorder::RecordMode ToFImageRecorder::GetRecordMode() { return this->m_RecordMode; } void ToFImageRecorder::SetRecordMode(ToFImageRecorder::RecordMode recordMode) { this->m_RecordMode = recordMode; } } diff --git a/Modules/ToFHardware/mitkToFImageRecorder.h b/Modules/ToFHardware/mitkToFImageRecorder.h index 16e022f396..e9ea462700 100644 --- a/Modules/ToFHardware/mitkToFImageRecorder.h +++ b/Modules/ToFHardware/mitkToFImageRecorder.h @@ -1,177 +1,178 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitkToFImageRecorder_h #define mitkToFImageRecorder_h #include "MitkToFHardwareExports.h" #include #include "mitkToFCameraDevice.h" #include "mitkToFImageCsvWriter.h" #include "mitkToFNrrdImageWriter.h" #include #include -#include #include +#include +#include + namespace mitk { /** * @brief Recorder class for ToF images * * This class represents a recorder for ToF data. A ToFCameraDevice is used to acquire the data. The acquired images * are then added to a ToFImageWriter that performs the actual writing. * * Recording can be performed either frame-based or continuously. * * @warning It is currently not guaranteed that all acquired images are recorded, since the recording * is done in a newly spawned thread. However, in practise only very few images are lost. See bug #12997 * for more details. * * @ingroup ToFHardware */ class MITKTOFHARDWARE_EXPORT ToFImageRecorder : public itk::Object { public: ToFImageRecorder(); ~ToFImageRecorder() override; mitkClassMacroItkParent( ToFImageRecorder , itk::Object ); itkFactorylessNewMacro(Self); itkCloneMacro(Self); itkGetMacro( DistanceImageFileName, std::string ); itkGetMacro( AmplitudeImageFileName, std::string ); itkGetMacro( IntensityImageFileName, std::string ); itkGetMacro( RGBImageFileName, std::string ); itkGetMacro( ToFCaptureWidth, int ); itkGetMacro( ToFCaptureHeight, int ); itkGetMacro( RGBCaptureWidth, int ); itkGetMacro( RGBCaptureHeight, int ); itkGetMacro( DistanceImageSelected, bool ); itkGetMacro( AmplitudeImageSelected, bool ); itkGetMacro( IntensityImageSelected, bool ); itkGetMacro( RGBImageSelected, bool ); itkGetMacro( NumOfFrames, int ); itkGetMacro( FileFormat, std::string ); itkSetMacro( DistanceImageFileName, std::string ); itkSetMacro( AmplitudeImageFileName, std::string ); itkSetMacro( IntensityImageFileName, std::string ); itkSetMacro(RGBImageFileName, std::string ); itkSetMacro( DistanceImageSelected, bool ); itkSetMacro( AmplitudeImageSelected, bool ); itkSetMacro( IntensityImageSelected, bool ); itkSetMacro( RGBImageSelected, bool ); itkSetMacro( NumOfFrames, int ); itkSetMacro( FileFormat, std::string ); enum RecordMode{ PerFrames, Infinite }; /*! \brief Returns the currently set RecordMode \return record mode: PerFrames ("Record specified number of frames"), Infinite ("Record until abort is required") */ ToFImageRecorder::RecordMode GetRecordMode(); /*! \brief Set the RecordMode \param recordMode: PerFrames ("Record specified number of frames"), Infinite ("Record until abort is required") */ void SetRecordMode(ToFImageRecorder::RecordMode recordMode); /*! \brief Set the device used for acquiring ToF images \param aToFCameraDevice ToF camera device used. */ void SetCameraDevice(ToFCameraDevice* aToFCameraDevice); /*! \brief Get the device used for acquiring ToF images \return ToF camera device used. */ ToFCameraDevice* GetCameraDevice(); /*! \brief Get the type of image to be recorded \return ToF image type: ToFImageType3D (0) or ToFImageType2DPlusT (1) */ ToFImageWriter::ToFImageType GetToFImageType(); /*! \brief Set the type of image to be recorded \param toFImageType type of the ToF image: ToFImageType3D (0) or ToFImageType2DPlusT (1) */ void SetToFImageType(ToFImageWriter::ToFImageType toFImageType); /*! \brief Starts the recording by spawning a Thread which streams the data to a file. Aborting of the record process is controlled by the m_Abort flag */ void StartRecording(); /*! \brief Stops the recording by setting the m_Abort flag to false */ void StopRecording(); /*! \brief Wait until thread is terinated */ void WaitForThreadBeingTerminated(); protected: /*! \brief Thread method acquiring data via the ToFCameraDevice and recording it to file via the ToFImageWriter */ - static ITK_THREAD_RETURN_TYPE RecordData(void* pInfoStruct); + void RecordData(); // data acquisition ToFCameraDevice::Pointer m_ToFCameraDevice; ///< ToFCameraDevice used for acquiring the images int m_ToFCaptureWidth; ///< width (x-dimension) of the images to record. int m_ToFCaptureHeight; ///< height (y-dimension) of the images to record. int m_ToFPixelNumber; ///< number of pixels (widht*height) of the images to record int m_RGBCaptureWidth; ///< width (x-dimension) of the images to record. int m_RGBCaptureHeight; ///< height (y-dimension) of the images to record. int m_RGBPixelNumber; ///< number of pixels (widht*height) of the images to record int m_SourceDataSize; ///< size of the source data provided by the device int m_ImageSequence; ///< number of images currently acquired float* m_IntensityArray; ///< array holding the intensity data float* m_DistanceArray; ///< array holding the distance data float* m_AmplitudeArray; ///< array holding the amplitude data unsigned char* m_RGBArray; ///< array holding the RGB data if available (e.g. for Kinect) char* m_SourceDataArray; ///< array holding the source data // data writing ToFImageWriter::Pointer m_ToFImageWriter; ///< image writer writing the acquired images to a file std::string m_DistanceImageFileName; ///< file name for saving the distance image std::string m_AmplitudeImageFileName; ///< file name for saving the amplitude image std::string m_IntensityImageFileName; ///< file name for saving the intensity image std::string m_RGBImageFileName; ///< file name for saving the rgb image int m_NumOfFrames; ///< number of frames to be recorded by this recorder ToFImageWriter::ToFImageType m_ToFImageType; ///< type of image to be recorded: ToFImageType3D (0) or ToFImageType2DPlusT (1) ToFImageRecorder::RecordMode m_RecordMode; ///< mode of recording the images: specified number of frames (PerFrames) or infinite (Infinite) std::string m_FileFormat; ///< file format for saving images. If .csv is chosen, ToFImageCsvWriter is used bool m_DistanceImageSelected; ///< flag indicating if distance image should be recorded bool m_AmplitudeImageSelected; ///< flag indicating if amplitude image should be recorded bool m_IntensityImageSelected; ///< flag indicating if intensity image should be recorded bool m_RGBImageSelected; ///< flag indicating if rgb image should be recorded // threading - itk::MultiThreader::Pointer m_MultiThreader; ///< member for thread-handling (ITK-based) - int m_ThreadID; ///< ID of the thread recording the data - itk::FastMutexLock::Pointer m_AbortMutex; ///< mutex for thread-safe data access of abort flag + std::thread m_Thread; + std::mutex m_AbortMutex; ///< mutex for thread-safe data access of abort flag bool m_Abort; ///< flag controlling the abort mechanism of the recording procedure. For thread-safety only use in combination with m_AbortMutex private: }; } //END mitk namespace #endif //mitkToFImageRecorder_h diff --git a/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp b/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp index 77174ebeda..356d726d67 100644 --- a/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp +++ b/Modules/ToFHardware/mitkToFNrrdImageWriter.cpp @@ -1,276 +1,276 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // mitk includes #include // itk includes #include "itksys/SystemTools.hxx" #include "itkNrrdImageIO.h" namespace mitk { ToFNrrdImageWriter::ToFNrrdImageWriter(): ToFImageWriter(), m_DistanceOutfile(), m_AmplitudeOutfile(), m_IntensityOutfile() { m_Extension = std::string(".nrrd"); } ToFNrrdImageWriter::~ToFNrrdImageWriter() { } void ToFNrrdImageWriter::Open() { this->CheckForFileExtension(this->m_DistanceImageFileName); this->CheckForFileExtension(this->m_AmplitudeImageFileName); this->CheckForFileExtension(this->m_IntensityImageFileName); this->CheckForFileExtension(this->m_RGBImageFileName); this->m_ToFPixelNumber = this->m_ToFCaptureWidth * this->m_ToFCaptureHeight; this->m_ToFImageSizeInBytes = this->m_ToFPixelNumber * sizeof(float); this->m_RGBPixelNumber = this->m_RGBCaptureWidth * this->m_RGBCaptureHeight; this->m_RGBImageSizeInBytes = this->m_RGBPixelNumber * sizeof(unsigned char) * 3; if (this->m_DistanceImageSelected) { this->OpenStreamFile( this->m_DistanceOutfile, this->m_DistanceImageFileName); } if (this->m_AmplitudeImageSelected) { this->OpenStreamFile(this->m_AmplitudeOutfile, this->m_AmplitudeImageFileName); } if (this->m_IntensityImageSelected) { this->OpenStreamFile(this->m_IntensityOutfile, this->m_IntensityImageFileName); } if (this->m_RGBImageSelected) { this->OpenStreamFile(this->m_RGBOutfile, this->m_RGBImageFileName); } this->m_NumOfFrames = 0; } void ToFNrrdImageWriter::Close() { if (this->m_DistanceImageSelected) { this->CloseStreamFile(this->m_DistanceOutfile, this->m_DistanceImageFileName); } if (this->m_AmplitudeImageSelected) { this->CloseStreamFile(this->m_AmplitudeOutfile, this->m_AmplitudeImageFileName); } if (this->m_IntensityImageSelected) { this->CloseStreamFile(this->m_IntensityOutfile, this->m_IntensityImageFileName); } if (this->m_RGBImageSelected) { this->CloseStreamFile(this->m_RGBOutfile, this->m_RGBImageFileName); } } void ToFNrrdImageWriter::Add(float* distanceFloatData, float* amplitudeFloatData, float* intensityFloatData, unsigned char* rgbData) { if (this->m_DistanceImageSelected) { this->m_DistanceOutfile.write( (char*) distanceFloatData, this->m_ToFImageSizeInBytes); } if (this->m_AmplitudeImageSelected) { this->m_AmplitudeOutfile.write( (char*)amplitudeFloatData, this->m_ToFImageSizeInBytes); } if (this->m_IntensityImageSelected) { this->m_IntensityOutfile.write(( char* )intensityFloatData, this->m_ToFImageSizeInBytes); } if (this->m_RGBImageSelected) { this->m_RGBOutfile.write(( char* )rgbData, this->m_RGBImageSizeInBytes); } this->m_NumOfFrames++; } void ToFNrrdImageWriter::OpenStreamFile( std::ofstream &outfile, std::string outfileName ) { outfile.open(outfileName.c_str(), std::ofstream::binary); if(!outfile.is_open()) { MITK_ERROR << "Error opening outfile: " << outfileName; throw std::logic_error("Error opening outfile."); return; } } void ToFNrrdImageWriter::CloseStreamFile( std::ofstream &outfile, std::string fileName ) { if (this->m_NumOfFrames == 0) { outfile.close(); throw std::logic_error("File is empty."); return; } // flush the last data to the file and convert the stream data to nrrd file outfile.flush(); this->ConvertStreamToNrrdFormat( fileName ); outfile.close(); } void ToFNrrdImageWriter::ConvertStreamToNrrdFormat( std::string fileName ) { int CaptureWidth = 0; int CaptureHeight = 0; int PixelNumber = 0; if (fileName==this->m_RGBImageFileName) { CaptureWidth = this->m_RGBCaptureWidth; CaptureHeight = this->m_RGBCaptureHeight; PixelNumber = this->m_RGBPixelNumber; } else { CaptureWidth = this->m_ToFCaptureWidth; CaptureHeight = this->m_ToFCaptureHeight; PixelNumber = this->m_ToFPixelNumber; } Image::Pointer imageTemplate = Image::New(); unsigned int dimension; unsigned int* dimensions; if(m_ToFImageType == ToFImageType2DPlusT) { dimension = 4; dimensions = new unsigned int[dimension]; dimensions[0] = CaptureWidth; dimensions[1] = CaptureHeight; dimensions[2] = 1; dimensions[3] = this->m_NumOfFrames; } else if( m_ToFImageType == ToFImageType3D) { dimension = 3; dimensions = new unsigned int[dimension]; dimensions[0] = CaptureWidth; dimensions[1] = CaptureHeight; dimensions[2] = this->m_NumOfFrames; } else { throw std::logic_error("No image type set, please choose between 2D+t and 3D!"); } float* floatData = nullptr; unsigned char* rgbData = nullptr; if (fileName==this->m_RGBImageFileName) { rgbData = new unsigned char[PixelNumber*3]; for(int i=0; i, 3>(); imageTemplate->Initialize( RGBType,dimension, dimensions, 1); imageTemplate->SetSlice(rgbData, 0, 0, 0); } else { floatData = new float[PixelNumber]; for(int i=0; i(); imageTemplate->Initialize( FloatType,dimension, dimensions, 1); imageTemplate->SetSlice(floatData, 0, 0, 0); } itk::NrrdImageIO::Pointer nrrdWriter = itk::NrrdImageIO::New(); nrrdWriter->SetNumberOfDimensions(dimension); nrrdWriter->SetPixelType( imageTemplate->GetPixelType().GetPixelType()); - nrrdWriter->SetComponentType( (itk::ImageIOBase::IOComponentType) imageTemplate->GetPixelType().GetComponentType()); + nrrdWriter->SetComponentType( imageTemplate->GetPixelType().GetComponentType()); if(imageTemplate->GetPixelType().GetNumberOfComponents() > 1) { nrrdWriter->SetNumberOfComponents(imageTemplate->GetPixelType().GetNumberOfComponents()); } itk::ImageIORegion ioRegion( dimension ); mitk::Vector3D spacing = imageTemplate->GetGeometry()->GetSpacing(); mitk::Point3D origin = imageTemplate->GetGeometry()->GetOrigin(); for(unsigned int i = 0; i < dimension; i++) { nrrdWriter->SetDimensions(i,dimensions[i]); nrrdWriter->SetSpacing(i,spacing[i]); nrrdWriter->SetOrigin(i,origin[i]); - mitk::Vector3D direction; - direction.SetVnlVector(imageTemplate->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); + mitk::Vector3D direction(0.0); + direction.SetVnlVector(imageTemplate->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i).as_ref()); vnl_vector< double > axisDirection(dimension); for(unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction[j]/spacing[i]; } nrrdWriter->SetDirection( i, axisDirection ); ioRegion.SetSize(i, imageTemplate->GetLargestPossibleRegion().GetSize(i) ); ioRegion.SetIndex(i, imageTemplate->GetLargestPossibleRegion().GetIndex(i) ); } nrrdWriter->SetIORegion(ioRegion); nrrdWriter->SetFileName(fileName); nrrdWriter->SetUseStreamedWriting(true); std::ifstream stream(fileName.c_str(), std::ifstream::binary); if (fileName==m_RGBImageFileName) { unsigned int size = PixelNumber*3 * this->m_NumOfFrames; unsigned int sizeInBytes = size * sizeof(unsigned char); unsigned char* data = new unsigned char[size]; stream.read((char*)data, sizeInBytes); nrrdWriter->Write(data); stream.close(); delete[] data; } else { unsigned int size = PixelNumber * this->m_NumOfFrames; unsigned int sizeInBytes = size * sizeof(float); float* data = new float[size]; stream.read((char*)data, sizeInBytes); try { nrrdWriter->Write(data); } catch (itk::ExceptionObject* e) { MITK_ERROR<< e->what(); return; } stream.close(); delete[] data; } delete[] dimensions; if (fileName==m_RGBImageFileName) { delete[] rgbData; } else { delete[] floatData; } } } // end namespace mitk diff --git a/Modules/TubeGraph/include/mitkTubeGraphDataInteractor.h b/Modules/TubeGraph/include/mitkTubeGraphDataInteractor.h index 8d07e4f3d8..622ed78781 100644 --- a/Modules/TubeGraph/include/mitkTubeGraphDataInteractor.h +++ b/Modules/TubeGraph/include/mitkTubeGraphDataInteractor.h @@ -1,122 +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. ============================================================================*/ #ifndef mitkTubeGraphDataInteractor3D_h_ #define mitkTubeGraphDataInteractor3D_h_ #include #include #include #include "mitkTubeGraph.h" #include "mitkTubeGraphProperty.h" namespace mitk { // Define events for TubeGraph interaction notifications - itkEventMacro(SelectionChangedTubeGraphEvent, itk::AnyEvent); + itkEventMacroDeclaration(SelectionChangedTubeGraphEvent, itk::AnyEvent); /** * \brief * * \ingroup Interaction */ // Inherit from DataInteratcor, this provides functionality of a state machine and configurable inputs. class MITKTUBEGRAPH_EXPORT TubeGraphDataInteractor : public DataInteractor { public: mitkClassMacro(TubeGraphDataInteractor, DataInteractor); itkNewMacro(Self); /** * Describes, which activation modes are available based on the * currently picked tube: * * \li None means "no tube is active" * \li Single means "only the picked tube is active" * \li ToRoot means "all tubes from the picked on down to the root of the tube graph are active" * \li ToPeriphery means "all tubes included in the subgraph of the currently picked vessel are active" * \li Points means "shortes path between two picked tubes are active" * \li Multiple means "all picked tubes are active" */ enum ActivationMode { None = 0, Single, ToRoot, ToPeriphery, Points, Multiple }; enum ActionMode { AttributationMode = 0, AnnotationMode, EditMode, RootMode, InformationMode }; void SetActivationMode(const ActivationMode &activationMode); ActivationMode GetActivationMode(); void SetActionMode(const ActionMode &actionMode); ActionMode GetActionMode(); void ResetPickedTubes(); mitk::Point3D GetLastPickedPosition(); protected: TubeGraphDataInteractor(); ~TubeGraphDataInteractor() override; /** * Here actions strings from the loaded state machine pattern are mapped to functions of * the DataInteractor. These functions are called when an action from the state machine pattern is executed. */ void ConnectActionsAndFunctions() override; /** * This function is called when a DataNode has been set/changed. */ void DataNodeChanged() override; /** * Initializes the movement, stores starting position. */ virtual bool CheckOverTube(const InteractionEvent *); virtual void SelectTube(StateMachineAction *, InteractionEvent *); virtual void DeselectTube(StateMachineAction *, InteractionEvent *); void SelectTubesByActivationModus(); void UpdateActivation(); private: std::vector GetTubesToRoot(); std::vector GetTubesBetweenPoints(); std::vector GetPathToPeriphery(); std::vector GetPathBetweenTubes(const TubeGraph::TubeDescriptorType &start, const TubeGraph::TubeDescriptorType &end); TubeGraph::Pointer m_TubeGraph; TubeGraphProperty::Pointer m_TubeGraphProperty; TubeGraph::TubeDescriptorType m_LastPickedTube; TubeGraph::TubeDescriptorType m_SecondLastPickedTube; ActivationMode m_ActivationMode; ActionMode m_ActionMode; mitk::TubeElement *m_LastPickedElement = nullptr; }; } #endif diff --git a/Modules/TubeGraph/src/Interactions/mitkTubeGraphDataInteractor.cpp b/Modules/TubeGraph/src/Interactions/mitkTubeGraphDataInteractor.cpp index 55cab1dbcb..bd0af564dd 100644 --- a/Modules/TubeGraph/src/Interactions/mitkTubeGraphDataInteractor.cpp +++ b/Modules/TubeGraph/src/Interactions/mitkTubeGraphDataInteractor.cpp @@ -1,285 +1,290 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkTubeGraphDataInteractor.h" #include #include #include #include "mitkTubeGraphPicker.h" #include #include #include #include #include +namespace mitk +{ + itkEventMacroDefinition(SelectionChangedTubeGraphEvent, itk::AnyEvent); +} + mitk::TubeGraphDataInteractor::TubeGraphDataInteractor() : m_LastPickedTube(TubeGraph::ErrorId), m_SecondLastPickedTube(TubeGraph::ErrorId), m_ActivationMode(None), m_ActionMode(AttributationMode) { } mitk::TubeGraphDataInteractor::~TubeGraphDataInteractor() {} void mitk::TubeGraphDataInteractor::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually // executing an action CONNECT_CONDITION("isOverTube", CheckOverTube); // **Function** in the statmachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectTube", SelectTube); CONNECT_FUNCTION("deselectTube", DeselectTube); } void mitk::TubeGraphDataInteractor::DataNodeChanged() { if (GetDataNode() != nullptr) { if (GetDataNode()->GetData() != nullptr) { m_TubeGraph = dynamic_cast(GetDataNode()->GetData()); m_TubeGraphProperty = dynamic_cast( m_TubeGraph->GetProperty("Tube Graph.Visualization Information").GetPointer()); if (m_TubeGraphProperty.IsNull()) MITK_ERROR << "Something went wrong! No tube graph property!"; } else m_TubeGraph = nullptr; } else m_TubeGraph = nullptr; } bool mitk::TubeGraphDataInteractor::CheckOverTube(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast(interactionEvent); if (positionEvent == nullptr) return false; auto *picker = new mitk::TubeGraphPicker(); picker->SetTubeGraph(m_TubeGraph); auto pickedTube = picker->GetPickedTube(positionEvent->GetPositionInWorld()); TubeGraph::TubeDescriptorType tubeDescriptor = pickedTube.first; if (tubeDescriptor != TubeGraph::ErrorId) { m_LastPickedElement = pickedTube.second; m_SecondLastPickedTube = m_LastPickedTube; m_LastPickedTube = tubeDescriptor; return true; } else // nothing picked return false; } void mitk::TubeGraphDataInteractor::SelectTube(StateMachineAction *, InteractionEvent *) { if (m_TubeGraph.IsNull()) return; this->SelectTubesByActivationModus(); RenderingManager::GetInstance()->RequestUpdateAll(); if (m_ActivationMode != None) { // show tube id on status bar std::stringstream displayText; displayText << "Picked tube: ID [" << m_LastPickedTube.first << "," << m_LastPickedTube.second << "]"; StatusBar::GetInstance()->DisplayText(displayText.str().c_str()); // TODO!!! this->InvokeEvent(SelectionChangedTubeGraphEvent()); } } void mitk::TubeGraphDataInteractor::DeselectTube(StateMachineAction *, InteractionEvent *) { if (m_TubeGraph.IsNull()) return; if ((m_ActivationMode != Multiple) && (m_ActivationMode != Points)) { m_TubeGraphProperty->DeactivateAllTubes(); RenderingManager::GetInstance()->RequestUpdateAll(); // TODO!!!this->InvokeEvent(SelectionChangedTubeGraphEvent()); } // show info on status bar StatusBar::GetInstance()->DisplayText("No tube hit!"); } void mitk::TubeGraphDataInteractor::SetActivationMode(const ActivationMode &activationMode) { m_ActivationMode = activationMode; if (m_TubeGraph.IsNotNull()) if (m_LastPickedTube != mitk::TubeGraph::ErrorId) this->UpdateActivation(); } mitk::TubeGraphDataInteractor::ActivationMode mitk::TubeGraphDataInteractor::GetActivationMode() { return m_ActivationMode; } void mitk::TubeGraphDataInteractor::SetActionMode(const ActionMode &actionMode) { m_ActionMode = actionMode; } mitk::TubeGraphDataInteractor::ActionMode mitk::TubeGraphDataInteractor::GetActionMode() { return m_ActionMode; } void mitk::TubeGraphDataInteractor::SelectTubesByActivationModus() { if (m_LastPickedTube != mitk::TubeGraph::ErrorId) { this->UpdateActivation(); } } void mitk::TubeGraphDataInteractor::UpdateActivation() { if (m_ActionMode == RootMode) { m_TubeGraphProperty->DeactivateAllTubes(); m_TubeGraphProperty->SetTubeActive(m_LastPickedTube, true); // QmitkTubeGraphSelectRootDialog* dialog = new QmitkTubeGraphSelectRootDialog(m_Parent); // int dialogReturnValue = dialog->exec(); // delete dialog; // if ( dialogReturnValue != QDialog::Rejected ) // user doesn't clicked cancel or pressed Esc or something similar //{ m_TubeGraph->SetRootTube(m_LastPickedTube); //} m_TubeGraphProperty->DeactivateAllTubes(); RenderingManager::GetInstance()->RequestUpdateAll(); } else { switch (m_ActivationMode) { case None: { m_TubeGraphProperty->DeactivateAllTubes(); } break; case Single: { m_TubeGraphProperty->DeactivateAllTubes(); m_TubeGraphProperty->SetTubeActive(m_LastPickedTube, true); } break; case Multiple: { // special deactivation for multiple modus // if activated--> deactivate; if not activated--> activate if (m_TubeGraphProperty->IsTubeActive(m_LastPickedTube)) m_TubeGraphProperty->SetTubeActive(m_LastPickedTube, false); else m_TubeGraphProperty->SetTubeActive(m_LastPickedTube, true); } break; case ToRoot: { m_TubeGraphProperty->DeactivateAllTubes(); std::vector activeTubes = this->GetTubesToRoot(); m_TubeGraphProperty->SetTubesActive(activeTubes); } break; case ToPeriphery: { m_TubeGraphProperty->DeactivateAllTubes(); std::vector activeTubes = this->GetPathToPeriphery(); m_TubeGraphProperty->SetTubesActive(activeTubes); } break; case Points: { m_TubeGraphProperty->DeactivateAllTubes(); std::vector activeTubes = this->GetTubesBetweenPoints(); m_TubeGraphProperty->SetTubesActive(activeTubes); } break; default: MITK_WARN << "Unknown tube graph interaction mode!"; break; } } } std::vector mitk::TubeGraphDataInteractor::GetTubesToRoot() { TubeGraph::TubeDescriptorType root = m_TubeGraph->GetRootTube(); if (root == TubeGraph::ErrorId) { root = m_TubeGraph->GetThickestTube(); m_TubeGraph->SetRootTube(root); } return this->GetPathBetweenTubes(m_LastPickedTube, root); } std::vector mitk::TubeGraphDataInteractor::GetTubesBetweenPoints() { return this->GetPathBetweenTubes(m_LastPickedTube, m_SecondLastPickedTube); } std::vector mitk::TubeGraphDataInteractor::GetPathBetweenTubes( const mitk::TubeGraph::TubeDescriptorType &start, const mitk::TubeGraph::TubeDescriptorType &end) { std::vector solutionPath; if ((start != TubeGraph::ErrorId) && (end != TubeGraph::ErrorId)) { if (start != end) solutionPath = m_TubeGraph->SearchAllPathBetweenVertices(start, end); else solutionPath.push_back(start); } return solutionPath; } std::vector mitk::TubeGraphDataInteractor::GetPathToPeriphery() { std::vector solutionPath; if (m_LastPickedTube != TubeGraph::ErrorId) solutionPath = m_TubeGraph->SearchPathToPeriphery(m_LastPickedTube); return solutionPath; } void mitk::TubeGraphDataInteractor::ResetPickedTubes() { m_LastPickedTube = TubeGraph::ErrorId; m_SecondLastPickedTube = TubeGraph::ErrorId; } mitk::Point3D mitk::TubeGraphDataInteractor::GetLastPickedPosition() { if (m_LastPickedElement) return m_LastPickedElement->GetCoordinates(); else return mitk::Point3D(); } diff --git a/Modules/US/USFilters/mitkUSImageSource.cpp b/Modules/US/USFilters/mitkUSImageSource.cpp index 2091d3733a..411a8bd76e 100644 --- a/Modules/US/USFilters/mitkUSImageSource.cpp +++ b/Modules/US/USFilters/mitkUSImageSource.cpp @@ -1,122 +1,121 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkUSImageSource.h" #include "mitkProperties.h" const char* mitk::USImageSource::IMAGE_PROPERTY_IDENTIFIER = "id_nummer"; mitk::USImageSource::USImageSource() : m_OpenCVToMitkFilter(mitk::OpenCVToMitkImageFilter::New()), m_MitkToOpenCVFilter(nullptr), m_ImageFilter(mitk::BasicCombinationOpenCVImageFilter::New()), - m_CurrentImageId(0), - m_ImageFilterMutex(itk::FastMutexLock::New()) + m_CurrentImageId(0) { } mitk::USImageSource::~USImageSource() { } void mitk::USImageSource::PushFilter(AbstractOpenCVImageFilter::Pointer filter) { m_ImageFilter->PushFilter(filter); } bool mitk::USImageSource::RemoveFilter(AbstractOpenCVImageFilter::Pointer filter) { return m_ImageFilter->RemoveFilter(filter); } bool mitk::USImageSource::GetIsFilterInThePipeline(AbstractOpenCVImageFilter::Pointer filter) { return m_ImageFilter->GetIsFilterOnTheList(filter); } std::vector mitk::USImageSource::GetNextImage() { std::vector result; // Apply OpenCV based filters beforehand if (m_ImageFilter.IsNotNull() && !m_ImageFilter->GetIsEmpty()) { std::vector imageVector; GetNextRawImage(imageVector); if(result.size() != imageVector.size()) result.resize(imageVector.size()); for (size_t i = 0; i < imageVector.size(); ++i) { if (!imageVector[i].empty()) { - m_ImageFilterMutex->Lock(); + m_ImageFilterMutex.lock(); m_ImageFilter->FilterImage(imageVector[i], m_CurrentImageId); - m_ImageFilterMutex->Unlock(); + m_ImageFilterMutex.unlock(); // convert to MITK image this->m_OpenCVToMitkFilter->SetOpenCVMat(imageVector[i]); this->m_OpenCVToMitkFilter->Update(); // OpenCVToMitkImageFilter returns a standard mitk::image. result[i] = this->m_OpenCVToMitkFilter->GetOutput(); } } } else { this->GetNextRawImage(result); } for (size_t i = 0; i < result.size(); ++i) { if (result[i].IsNotNull()) { result[i]->SetProperty(IMAGE_PROPERTY_IDENTIFIER, mitk::IntProperty::New(m_CurrentImageId)); } else { //MITK_WARN("mitkUSImageSource") << "Result image " << i << " is not set."; result[i] = mitk::Image::New(); } } m_CurrentImageId++; return result; } void mitk::USImageSource::GetNextRawImage(std::vector& imageVector) { // create filter object if it does not exist yet if (!m_MitkToOpenCVFilter) { m_MitkToOpenCVFilter = mitk::ImageToOpenCVImageFilter::New(); } // get mitk image through virtual method of the subclass std::vector mitkImg; this->GetNextRawImage(mitkImg); for (unsigned int i = 0; i < mitkImg.size(); ++i) { if (mitkImg[i].IsNull() || !mitkImg[i]->IsInitialized()) { imageVector[i] = cv::Mat(); } else { // convert mitk::Image to an OpenCV image m_MitkToOpenCVFilter->SetImage(mitkImg[i]); imageVector[i] = m_MitkToOpenCVFilter->GetOpenCVMat(); } } } diff --git a/Modules/US/USFilters/mitkUSImageSource.h b/Modules/US/USFilters/mitkUSImageSource.h index 7c66c5e2e4..c11147f1b2 100644 --- a/Modules/US/USFilters/mitkUSImageSource.h +++ b/Modules/US/USFilters/mitkUSImageSource.h @@ -1,98 +1,99 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKUSImageSource_H_HEADER_INCLUDED_ #define MITKUSImageSource_H_HEADER_INCLUDED_ +#include + // ITK #include -#include // MITK #include #include #include "mitkBasicCombinationOpenCVImageFilter.h" #include "mitkOpenCVToMitkImageFilter.h" #include "mitkImageToOpenCVImageFilter.h" namespace mitk { /** * \brief This is an abstract superclass for delivering USImages. * Each subclass must implement the method mitk::USImageSource::GetNextRawImage(). * The public method mitk::USImageSource::GetNextImage() can the be used to * get the next image from the image source. This image will be filtered by * the filter set with mitk::USImageSource::SetImageFilter(). * * \ingroup US */ class MITKUS_EXPORT USImageSource : public itk::Object { public: static const char* IMAGE_PROPERTY_IDENTIFIER; mitkClassMacroItkParent(USImageSource, itk::Object); itkGetMacro(ImageFilter, mitk::BasicCombinationOpenCVImageFilter::Pointer); void PushFilter(AbstractOpenCVImageFilter::Pointer filter); bool RemoveFilter(AbstractOpenCVImageFilter::Pointer filter); bool GetIsFilterInThePipeline(AbstractOpenCVImageFilter::Pointer filter); /** * \brief Retrieves the next frame. This will typically be the next frame * in a file or the last cached file in a device. The image is filtered if * a filter was set by mitk::USImageSource::SetImageFilter(). * * \return pointer to the next USImage (filtered if set) */ std::vector GetNextImage(); protected: USImageSource(); ~USImageSource() override; /** * \brief Set the given OpenCV image matrix to the next image received * from the device or file. * * The standard implementation calls the overloaded function with an * mitk::Image and converts this image to OpenCV then. One should reimplement * this method for a better performance if an image filter is set. */ virtual void GetNextRawImage(std::vector&); /** * \brief Set mitk::Image to the next image received from the device or file. * This method must be implemented in every subclass. */ virtual void GetNextRawImage(std::vector&) = 0; /** * \brief Used to convert from OpenCV Images to MITK Images. */ mitk::OpenCVToMitkImageFilter::Pointer m_OpenCVToMitkFilter; /** * \brief Used to convert from MITK Images to OpenCV Images. */ mitk::ImageToOpenCVImageFilter::Pointer m_MitkToOpenCVFilter; private: /** * \brief Filter is executed during mitk::USImageVideoSource::GetNextImage(). */ BasicCombinationOpenCVImageFilter::Pointer m_ImageFilter; int m_CurrentImageId; - itk::FastMutexLock::Pointer m_ImageFilterMutex; + std::mutex m_ImageFilterMutex; }; } // namespace mitk #endif /* MITKUSImageSource_H_HEADER_INCLUDED_ */ diff --git a/Modules/US/USModel/mitkUSDevice.cpp b/Modules/US/USModel/mitkUSDevice.cpp index 1f4ef2a873..a88bcecb6a 100644 --- a/Modules/US/USModel/mitkUSDevice.cpp +++ b/Modules/US/USModel/mitkUSDevice.cpp @@ -1,713 +1,683 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkUSDevice.h" #include "mitkImageReadAccessor.h" // US Control Interfaces #include "mitkUSControlInterfaceProbes.h" #include "mitkUSControlInterfaceBMode.h" #include "mitkUSControlInterfaceDoppler.h" // Microservices #include #include #include #include mitk::USDevice::PropertyKeys mitk::USDevice::GetPropertyKeys() { static mitk::USDevice::PropertyKeys propertyKeys; return propertyKeys; } mitk::USDevice::USImageCropArea mitk::USDevice::GetCropArea() { MITK_INFO << "Return Crop Area L:" << m_CropArea.cropLeft << " R:" << m_CropArea.cropRight << " T:" << m_CropArea.cropTop << " B:" << m_CropArea.cropBottom; return m_CropArea; } unsigned int mitk::USDevice::GetSizeOfImageVector() { return m_ImageVector.size(); } mitk::USDevice::USDevice(std::string manufacturer, std::string model) : mitk::ImageSource(), - m_FreezeBarrier(nullptr), - m_FreezeMutex(), - m_MultiThreader(itk::MultiThreader::New()), - m_ImageMutex(itk::FastMutexLock::New()), - m_ThreadID(-1), m_ImageVector(), m_Spacing(), m_IGTLServer(nullptr), m_IGTLMessageProvider(nullptr), m_ImageToIGTLMsgFilter(nullptr), m_IsFreezed(false), m_DeviceState(State_NoState), m_NumberOfOutputs(1), m_ServiceProperties(), m_ServiceRegistration(), m_Manufacturer(manufacturer), m_Name(model), m_Comment(), m_SpawnAcquireThread(true), m_UnregisteringStarted(false) { USImageCropArea empty; empty.cropBottom = 0; empty.cropTop = 0; empty.cropLeft = 0; empty.cropRight = 0; this->m_CropArea = empty; // set number of outputs this->SetNumberOfIndexedOutputs(m_NumberOfOutputs); // create a new output mitk::Image::Pointer newOutput = mitk::Image::New(); this->SetNthOutput(0, newOutput); } mitk::USDevice::USDevice(mitk::USImageMetadata::Pointer metadata) : mitk::ImageSource(), - m_FreezeBarrier(nullptr), - m_FreezeMutex(), - m_MultiThreader(itk::MultiThreader::New()), - m_ImageMutex(itk::FastMutexLock::New()), - m_ThreadID(-1), m_ImageVector(), m_Spacing(), m_IGTLServer(nullptr), m_IGTLMessageProvider(nullptr), m_ImageToIGTLMsgFilter(nullptr), m_IsFreezed(false), m_DeviceState(State_NoState), m_NumberOfOutputs(1), m_ServiceProperties(), m_ServiceRegistration(), m_SpawnAcquireThread(true), m_UnregisteringStarted(false) { m_Manufacturer = metadata->GetDeviceManufacturer(); m_Name = metadata->GetDeviceModel(); m_Comment = metadata->GetDeviceComment(); USImageCropArea empty; empty.cropBottom = 0; empty.cropTop = 0; empty.cropLeft = 0; empty.cropRight = 0; this->m_CropArea = empty; // set number of outputs this->SetNumberOfIndexedOutputs(m_NumberOfOutputs); // create a new output mitk::Image::Pointer newOutput = mitk::Image::New(); this->SetNthOutput(0, newOutput); } mitk::USDevice::~USDevice() { - if (m_ThreadID >= 0) - { - m_MultiThreader->TerminateThread(m_ThreadID); - } + if (m_Thread.joinable()) + m_Thread.detach(); // make sure that the us device is not registered at the micro service // anymore after it is destructed this->UnregisterOnService(); } mitk::USAbstractControlInterface::Pointer mitk::USDevice::GetControlInterfaceCustom() { MITK_INFO << "Custom control interface does not exist for this object."; return nullptr; } mitk::USControlInterfaceBMode::Pointer mitk::USDevice::GetControlInterfaceBMode() { MITK_INFO << "Control interface BMode does not exist for this object."; return nullptr; } mitk::USControlInterfaceProbes::Pointer mitk::USDevice::GetControlInterfaceProbes() { MITK_INFO << "Control interface Probes does not exist for this object."; return nullptr; } mitk::USControlInterfaceDoppler::Pointer mitk::USDevice::GetControlInterfaceDoppler() { MITK_INFO << "Control interface Doppler does not exist for this object."; return nullptr; } void mitk::USDevice::SetManufacturer(std::string manufacturer) { m_Manufacturer = manufacturer; if (m_DeviceState >= State_Initialized) { this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_MANUFACTURER, manufacturer); } } void mitk::USDevice::SetName(std::string name) { m_Name = name; if (m_DeviceState >= State_Initialized) { this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_NAME, name); } } void mitk::USDevice::SetComment(std::string comment) { m_Comment = comment; if (m_DeviceState >= State_Initialized) { this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_COMMENT, comment); } } us::ServiceProperties mitk::USDevice::ConstructServiceProperties() { mitk::USDevice::PropertyKeys propertyKeys = mitk::USDevice::GetPropertyKeys(); us::ServiceProperties props; props[propertyKeys.US_PROPKEY_ISCONNECTED] = this->GetIsConnected() ? "true" : "false"; props[propertyKeys.US_PROPKEY_ISACTIVE] = this->GetIsActive() ? "true" : "false"; props[propertyKeys.US_PROPKEY_LABEL] = this->GetServicePropertyLabel(); // get identifier of selected probe if there is one selected mitk::USControlInterfaceProbes::Pointer probesControls = this->GetControlInterfaceProbes(); if (probesControls.IsNotNull() && probesControls->GetIsActive()) { mitk::USProbe::Pointer probe = probesControls->GetSelectedProbe(); if (probe.IsNotNull()) { props[propertyKeys.US_PROPKEY_PROBES_SELECTED] = probe->GetName(); } } props[propertyKeys.US_PROPKEY_CLASS] = GetDeviceClass(); props[propertyKeys.US_PROPKEY_MANUFACTURER] = m_Manufacturer; props[propertyKeys.US_PROPKEY_NAME] = m_Name; props[propertyKeys.US_PROPKEY_COMMENT] = m_Comment; m_ServiceProperties = props; return props; } void mitk::USDevice::UnregisterOnService() { // unregister on micro service if (m_ServiceRegistration && !m_UnregisteringStarted) { // make sure that unregister is not started a second // time due to a callback during unregister for example m_UnregisteringStarted = true; m_ServiceRegistration.Unregister(); m_ServiceRegistration = 0; } } bool mitk::USDevice::Initialize() { if (!this->OnInitialization()) { return false; } m_DeviceState = State_Initialized; // Get Context and Module us::ModuleContext* context = us::GetModuleContext(); us::ServiceProperties props = this->ConstructServiceProperties(); m_ServiceRegistration = context->RegisterService(this, props); return true; } bool mitk::USDevice::Connect() { MITK_DEBUG << "mitk::USDevice::Connect() called"; if (this->GetIsConnected()) { MITK_INFO("mitkUSDevice") << "Tried to connect an ultrasound device that " "was already connected. Ignoring call..."; return true; } if (!this->GetIsInitialized()) { MITK_ERROR("mitkUSDevice") << "Cannot connect device if it is not in initialized state."; return false; } // Prepare connection, fail if this fails. if (!this->OnConnection()) { return false; } // Update state m_DeviceState = State_Connected; this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISCONNECTED, true); return true; } void mitk::USDevice::ConnectAsynchron() { - this->m_MultiThreader->SpawnThread(this->ConnectThread, this); + m_Thread = std::thread(&USDevice::ConnectThread, this); } bool mitk::USDevice::Disconnect() { if (!GetIsConnected()) { MITK_WARN << "Tried to disconnect an ultrasound device that was not " "connected. Ignoring call..."; return false; } // Prepare connection, fail if this fails. if (!this->OnDisconnection()) return false; // Update state m_DeviceState = State_Initialized; this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISCONNECTED, false); return true; } bool mitk::USDevice::Activate() { if (!this->GetIsConnected()) { MITK_INFO("mitkUSDevice") << "Cannot activate device if it is not in connected state."; return true; } if (OnActivation()) { m_DeviceState = State_Activated; - m_FreezeBarrier = itk::ConditionVariable::New(); - // spawn thread for aquire images if us device is active if (m_SpawnAcquireThread) { - this->m_ThreadID = - this->m_MultiThreader->SpawnThread(this->Acquire, this); + m_Thread = std::thread(&USDevice::Acquire, this); } this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE, true); this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL, this->GetServicePropertyLabel()); // initialize the b mode control properties of the micro service mitk::USControlInterfaceBMode::Pointer bmodeControls = this->GetControlInterfaceBMode(); if (bmodeControls.IsNotNull()) { bmodeControls->Initialize(); } } this->ProvideViaOIGTL(); return m_DeviceState == State_Activated; } void mitk::USDevice::ProvideViaOIGTL() { // create a new OpenIGTLink Server if (m_IGTLServer.IsNull()) m_IGTLServer = mitk::IGTLServer::New(true); m_IGTLServer->SetName(this->GetName()); // create a new OpenIGTLink Device source if (m_IGTLMessageProvider.IsNull()) m_IGTLMessageProvider = mitk::IGTLMessageProvider::New(); // set the OpenIGTLink server as the source for the device source m_IGTLMessageProvider->SetIGTLDevice(m_IGTLServer); // register the provider so that it can be configured with the IGTL manager // plugin. This could be hardcoded but now I already have the fancy plugin. m_IGTLMessageProvider->RegisterAsMicroservice(); m_ImageToIGTLMsgFilter = mitk::ImageToIGTLMessageFilter::New(); m_ImageToIGTLMsgFilter->ConnectTo(this); // set the name of this filter to identify it easier m_ImageToIGTLMsgFilter->SetName(this->GetName()); // register this filter as micro service. The message provider looks for // provided IGTLMessageSources, once it found this microservice and someone // requested this data type then the provider will connect with this filter // automatically. m_ImageToIGTLMsgFilter->RegisterAsMicroservice(); } void mitk::USDevice::Deactivate() { if (!this->GetIsActive()) { MITK_WARN("mitkUSDevice") << "Cannot deactivate a device which is not activae."; return; } if (!OnDeactivation()) { return; } DisableOIGTL(); m_DeviceState = State_Connected; this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_ISACTIVE, false); this->UpdateServiceProperty( mitk::USDevice::GetPropertyKeys().US_PROPKEY_LABEL, this->GetServicePropertyLabel()); } void mitk::USDevice::DisableOIGTL() { // TODO: This seems not to be enough cleanup to catch all cases. For example, if the device is disconnected // from the OIGTL GUI, this won't get cleaned up correctly. m_IGTLServer->CloseConnection(); m_IGTLMessageProvider->UnRegisterMicroservice(); m_ImageToIGTLMsgFilter->UnRegisterMicroservice(); } void mitk::USDevice::SetIsFreezed(bool freeze) { if (!this->GetIsActive()) { MITK_WARN("mitkUSDevice") << "Cannot freeze or unfreeze if device is not active."; return; } this->OnFreeze(freeze); if (freeze) { + std::lock_guard lock(m_FreezeMutex); m_IsFreezed = true; } else { - m_IsFreezed = false; + { + std::lock_guard lock(m_FreezeMutex); + m_IsFreezed = false; + } // wake up the image acquisition thread - m_FreezeBarrier->Signal(); + m_FreezeBarrier.notify_one(); } } bool mitk::USDevice::GetIsFreezed() { /* if (!this->GetIsActive()) { MITK_WARN("mitkUSDevice")("mitkUSTelemedDevice") << "Cannot get freeze state if the hardware interface is not ready. " "Returning false..."; return false; }*/ return m_IsFreezed; } void mitk::USDevice::PushFilter(AbstractOpenCVImageFilter::Pointer filter) { mitk::USImageSource::Pointer imageSource = this->GetUSImageSource(); if (imageSource.IsNull()) { MITK_ERROR << "ImageSource must not be null when pushing a filter."; mitkThrow() << "ImageSource must not be null when pushing a filter."; } imageSource->PushFilter(filter); } void mitk::USDevice::PushFilterIfNotPushedBefore( AbstractOpenCVImageFilter::Pointer filter) { mitk::USImageSource::Pointer imageSource = this->GetUSImageSource(); if (imageSource.IsNull()) { MITK_ERROR << "ImageSource must not be null when pushing a filter."; mitkThrow() << "ImageSource must not be null when pushing a filter."; } if (!imageSource->GetIsFilterInThePipeline(filter)) { imageSource->PushFilter(filter); } } bool mitk::USDevice::RemoveFilter(AbstractOpenCVImageFilter::Pointer filter) { mitk::USImageSource::Pointer imageSource = this->GetUSImageSource(); if (imageSource.IsNull()) { MITK_ERROR << "ImageSource must not be null when pushing a filter."; mitkThrow() << "ImageSource must not be null when removing a filter."; } return imageSource->RemoveFilter(filter); } void mitk::USDevice::UpdateServiceProperty(std::string key, std::string value) { m_ServiceProperties[key] = value; m_ServiceRegistration.SetProperties(m_ServiceProperties); // send event to notify listeners about the changed property m_PropertyChangedMessage(key, value); } void mitk::USDevice::UpdateServiceProperty(std::string key, double value) { std::stringstream stream; stream << value; this->UpdateServiceProperty(key, stream.str()); } void mitk::USDevice::UpdateServiceProperty(std::string key, bool value) { this->UpdateServiceProperty( key, value ? std::string("true") : std::string("false")); } /** mitk::Image* mitk::USDevice::GetOutput() { if (this->GetNumberOfOutputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetPrimaryOutput()); } mitk::Image* mitk::USDevice::GetOutput(unsigned int idx) { if (this->GetNumberOfOutputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetOutput(idx)); } void mitk::USDevice::GraftOutput(itk::DataObject *graft) { this->GraftNthOutput(0, graft); } void mitk::USDevice::GraftNthOutput(unsigned int idx, itk::DataObject *graft) { if ( idx >= this->GetNumberOfOutputs() ) { itkExceptionMacro(<<"Requested to graft output " << idx << " but this filter only has " << this->GetNumberOfOutputs() << " Outputs."); } if ( !graft ) { itkExceptionMacro(<<"Requested to graft output with a nullptr pointer object" ); } itk::DataObject* output = this->GetOutput(idx); if ( !output ) { itkExceptionMacro(<<"Requested to graft output that is a nullptr pointer" ); } // Call Graft on USImage to copy member data output->Graft( graft ); } */ void mitk::USDevice::GrabImage() { std::vector image = this->GetUSImageSource()->GetNextImage(); - m_ImageMutex->Lock(); + m_ImageMutex.lock(); this->SetImageVector(image); - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); } //########### GETTER & SETTER ##################// bool mitk::USDevice::GetIsInitialized() { return m_DeviceState == State_Initialized; } bool mitk::USDevice::GetIsActive() { return m_DeviceState == State_Activated; } bool mitk::USDevice::GetIsConnected() { return m_DeviceState == State_Connected; } std::string mitk::USDevice::GetDeviceManufacturer() { return m_Manufacturer; } std::string mitk::USDevice::GetDeviceModel() { return m_Name; } std::string mitk::USDevice::GetDeviceComment() { return m_Comment; } void mitk::USDevice::SetSpacing(double xSpacing, double ySpacing) { m_Spacing[0] = xSpacing; m_Spacing[1] = ySpacing; m_Spacing[2] = 1; if( m_ImageVector.size() > 0 ) { for( size_t index = 0; index < m_ImageVector.size(); ++index ) { auto& image = m_ImageVector[index]; if( image.IsNotNull() && image->IsInitialized() ) { image->GetGeometry()->SetSpacing(m_Spacing); } } this->Modified(); } MITK_INFO << "Spacing: " << m_Spacing; } void mitk::USDevice::GenerateData() { - m_ImageMutex->Lock(); + m_ImageMutex.lock(); for (unsigned int i = 0; i < m_ImageVector.size() && i < this->GetNumberOfIndexedOutputs(); ++i) { auto& image = m_ImageVector[i]; if (image.IsNull() || !image->IsInitialized()) { // skip image } else { mitk::Image::Pointer output = this->GetOutput(i); if (!output->IsInitialized() || output->GetDimension(0) != image->GetDimension(0) || output->GetDimension(1) != image->GetDimension(1) || output->GetDimension(2) != image->GetDimension(2) || output->GetPixelType() != image->GetPixelType()) { output->Initialize(image->GetPixelType(), image->GetDimension(), image->GetDimensions()); } // copy contents of the given image into the member variable mitk::ImageReadAccessor inputReadAccessor(image); output->SetImportVolume(inputReadAccessor.GetData()); output->SetGeometry(image->GetGeometry()); } } - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); }; std::string mitk::USDevice::GetServicePropertyLabel() { std::string isActive; if (this->GetIsActive()) { isActive = " (Active)"; } else { isActive = " (Inactive)"; } // e.g.: Zonare MyLab5 (Active) return m_Manufacturer + " " + m_Name + isActive; } -ITK_THREAD_RETURN_TYPE mitk::USDevice::Acquire(void* pInfoStruct) +void mitk::USDevice::Acquire() { - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct* pInfo = - (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - mitk::USDevice* device = (mitk::USDevice*)pInfo->UserData; - while (device->GetIsActive()) + while (this->GetIsActive()) { // lock this thread when ultrasound device is freezed - if (device->m_IsFreezed) - { - itk::SimpleMutexLock* mutex = &(device->m_FreezeMutex); - mutex->Lock(); + std::unique_lock lock(m_FreezeMutex); + m_FreezeBarrier.wait(lock, [this] { return !m_IsFreezed; }); - if (device->m_FreezeBarrier.IsNotNull()) - { - device->m_FreezeBarrier->Wait(mutex); - } - } - device->GrabImage(); + this->GrabImage(); } - return ITK_THREAD_RETURN_VALUE; } -ITK_THREAD_RETURN_TYPE mitk::USDevice::ConnectThread(void* pInfoStruct) +void mitk::USDevice::ConnectThread() { - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct* pInfo = - (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - mitk::USDevice* device = (mitk::USDevice*)pInfo->UserData; - - device->Connect(); - - return ITK_THREAD_RETURN_VALUE; + this->Connect(); } void mitk::USDevice::ProbeChanged(std::string probename) { this->UpdateServiceProperty(mitk::USDevice::GetPropertyKeys().US_PROPKEY_PROBES_SELECTED, probename); } void mitk::USDevice::DepthChanged(double depth) { this->UpdateServiceProperty(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH, depth); } diff --git a/Modules/US/USModel/mitkUSDevice.h b/Modules/US/USModel/mitkUSDevice.h index 720016b004..122b61d828 100644 --- a/Modules/US/USModel/mitkUSDevice.h +++ b/Modules/US/USModel/mitkUSDevice.h @@ -1,531 +1,532 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKUSDevice_H_HEADER_INCLUDED_ #define MITKUSDevice_H_HEADER_INCLUDED_ // STL +#include +#include +#include #include // MitkUS #include "mitkUSProbe.h" #include #include "mitkUSImageSource.h" // MitkIGTL #include "mitkIGTLMessageProvider.h" #include "mitkIGTLServer.h" #include "mitkIGTLDeviceSource.h" #include "mitkImageToIGTLMessageFilter.h" // MITK #include #include #include // ITK #include -#include // Microservices #include #include #include // DEPRECATED #include "mitkUSImageMetadata.h" namespace itk { template class SmartPointer; } namespace mitk { class USAbstractControlInterface; class USControlInterfaceBMode; class USControlInterfaceProbes; class USControlInterfaceDoppler; /** * \brief A device holds information about it's model, make and the connected probes. It is the * common super class for all devices and acts as an image source for mitkUSImages. It is the base class * for all US Devices, and every new device should extend it. * * US Devices support output of calibrated images, i.e. images that include a specific geometry. * To achieve this, call SetCalibration, and make sure that the subclass also calls apply * transformation at some point (The USDevice does not automatically apply the transformation to the image) * * Note that USDevices will be removed from micro servive when their * destructor is called. Registering into micro service is done when * mitk::USDevice::Initialize() is called. * * \ingroup US */ class MITKUS_EXPORT USDevice : public mitk::ImageSource { public: enum DeviceStates { State_NoState, State_Initialized, State_Connected, State_Activated }; mitkClassMacro(USDevice, mitk::ImageSource); itkSetMacro(SpawnAcquireThread, bool); itkGetMacro(SpawnAcquireThread, bool); struct USImageCropArea { int cropLeft; int cropRight; int cropBottom; int cropTop; }; /** * \brief These constants are used in conjunction with Microservices. * The constants aren't defined as static member attributes to avoid the * "static initialization order fiasco", which would occur when objects of * this class are used in module activators (for restoring stored device, * for example). */ struct PropertyKeys { const std::string US_INTERFACE_NAME; // Common Interface name of all US Devices. Used to refer to this device via Microservices const std::string US_PROPKEY_MANUFACTURER; const std::string US_PROPKEY_NAME; const std::string US_PROPKEY_COMMENT; const std::string US_PROPKEY_LABEL; // Human readable text represntation of this device const std::string US_PROPKEY_ISCONNECTED; // Whether this device is connected or not. const std::string US_PROPKEY_ISACTIVE; // Whether this device is active or not. const std::string US_PROPKEY_CLASS; // Class Name of this Object const std::string US_PROPKEY_PROBES_SELECTED; const std::string US_PROPKEY_BMODE_FREQUENCY; const std::string US_PROPKEY_BMODE_POWER; const std::string US_PROPKEY_BMODE_DEPTH; const std::string US_PROPKEY_BMODE_GAIN; const std::string US_PROPKEY_BMODE_REJECTION; const std::string US_PROPKEY_BMODE_DYNAMIC_RANGE; PropertyKeys() : US_INTERFACE_NAME("org.mitk.services.UltrasoundDevice"), US_PROPKEY_MANUFACTURER(US_INTERFACE_NAME + ".manufacturer"), US_PROPKEY_NAME(US_INTERFACE_NAME + ".name"), US_PROPKEY_COMMENT(US_INTERFACE_NAME + ".comment"), US_PROPKEY_LABEL(US_INTERFACE_NAME + ".label"), US_PROPKEY_ISCONNECTED(US_INTERFACE_NAME + ".isConnected"), US_PROPKEY_ISACTIVE(US_INTERFACE_NAME + ".isActive"), US_PROPKEY_CLASS(US_INTERFACE_NAME + ".class"), US_PROPKEY_PROBES_SELECTED(US_INTERFACE_NAME + ".probes.selected"), US_PROPKEY_BMODE_FREQUENCY(US_INTERFACE_NAME + ".bmode.frequency"), US_PROPKEY_BMODE_POWER(US_INTERFACE_NAME + ".bmode.power"), US_PROPKEY_BMODE_DEPTH(US_INTERFACE_NAME + ".bmode.depth"), US_PROPKEY_BMODE_GAIN(US_INTERFACE_NAME + ".bmode.gain"), US_PROPKEY_BMODE_REJECTION(US_INTERFACE_NAME + ".bmode.rejection"), US_PROPKEY_BMODE_DYNAMIC_RANGE(US_INTERFACE_NAME + ".bmode.dynamicRange") {} }; /** * \brief Event for being notified about changes of the micro service properties. * This event can be used if no micro service context is available. */ mitkNewMessage2Macro(PropertyChanged, const std::string&, const std::string&); /** * \return keys for the microservice properties of ultrasound devices */ static mitk::USDevice::PropertyKeys GetPropertyKeys(); /** * \brief Default getter for the custom control interface. * Has to be implemented in a subclass if a custom control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceCustom(); /** * \brief Default getter for the b mode control interface. * Has to be implemented in a subclass if a b mode control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceBMode(); /** * \brief Default getter for the probes control interface. * Has to be implemented in a subclass if a probes control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceProbes(); /** * \brief Default getter for the doppler control interface. * Has to be implemented in a subclass if a doppler control interface is * available. Default implementation returns null. * * \return null pointer */ virtual itk::SmartPointer GetControlInterfaceDoppler(); /** * \brief Changes device state to mitk::USDevice::State_Initialized. * During initialization the virtual method * mitk::USDevice::OnInitialization will be called. If this method * returns false the initialization process will be canceled. Otherwise * the mitk::USDevice is registered in a micro service. */ bool Initialize(); /** * \brief Connects this device. A connected device is ready to deliver images (i.e. be Activated). A Connected Device can be active. A disconnected Device cannot be active. * Internally calls onConnect and then registers the device with the service. A device usually should * override the OnConnection() method, but never the Connect() method, since this will possibly exclude the device * from normal service management. The exact flow of events is: * 0. Check if the device is already connected. If yes, return true anyway, but don't do anything. * 1. Call OnConnection() Here, a device should establish it's connection with the hardware Afterwards, it should be ready to start transmitting images at any time. * 2. If OnConnection() returns true ("successful"), then the device is registered with the service. * 3. if not, it the method itself returns false or may throw an expection, depeneding on the device implementation. * */ bool Connect(); void ConnectAsynchron(); /** * \brief Works analogously to mitk::USDevice::Connect(). Don't override this Method, but onDisconnection instead. */ bool Disconnect(); /** * \brief Activates this device. * After the activation process, the device will start to produce images. * This Method will fail, if the device is not connected. */ bool Activate(); /** * \brief Deactivates this device. * After the deactivation process, the device will no longer produce * images, but still be connected. */ void Deactivate(); /** * \brief Can toggle if ultrasound image is currently updated or freezed. * * \param freeze true to stop updating the ultrasound image, false to start updating again */ virtual void SetIsFreezed(bool freeze); /** * \return true if device is currently freezed (no image update is done), false otherwise */ virtual bool GetIsFreezed(); void PushFilter(AbstractOpenCVImageFilter::Pointer filter); void PushFilterIfNotPushedBefore(AbstractOpenCVImageFilter::Pointer filter); bool RemoveFilter(AbstractOpenCVImageFilter::Pointer filter); /** * @brief To be called when the used probe changed. Will update the service properties * @param probename of the now used probe */ void ProbeChanged(std::string probename); /** * @brief To be called when the scanning depth of the probe changed. Will update the service properties * @param depth that is now used */ void DepthChanged(double depth); /** * \brief Given property is updated in the device micro service. * This method is mainly for being used by the control interface * superclasses. You do not need to call it by yoursefs in your * concrete control interface classes. */ void UpdateServiceProperty(std::string key, std::string value); void UpdateServiceProperty(std::string key, double value); void UpdateServiceProperty(std::string key, bool value); //########### GETTER & SETTER ##################// /** * \brief Returns the Class of the Device. This Method must be reimplemented by every Inheriting Class. */ virtual std::string GetDeviceClass() = 0; /** * \brief True, if the device object is created and initialized, false otherwise. */ bool GetIsInitialized(); /** * \brief True, if the device is currently generating image data, false otherwise. */ bool GetIsActive(); /** * \brief True, if the device is currently ready to start transmitting image data or is already * transmitting image data. A disconnected device cannot be activated. */ bool GetIsConnected(); /* @return Returns the area that will be cropped from the US image. Is disabled / [0,0,0,0] by default. */ mitk::USDevice::USImageCropArea GetCropArea(); /* @return Returns the size of the m_ImageVector of the ultrasound device.*/ unsigned int GetSizeOfImageVector(); /** @return Returns the current image source of this device. */ virtual USImageSource::Pointer GetUSImageSource() = 0; /** \brief Deprecated -> use GetManufacturer() instead */ DEPRECATED(std::string GetDeviceManufacturer()); /** \brief Deprecated -> use GetName() instead */ DEPRECATED(std::string GetDeviceModel()); /** \brief Deprecated -> use GetCommend() instead */ DEPRECATED(std::string GetDeviceComment()); itkGetMacro(Manufacturer, std::string); itkGetMacro(Name, std::string); itkGetMacro(Comment, std::string); void SetManufacturer(std::string manufacturer); void SetName(std::string name); void SetComment(std::string comment); itkGetMacro(DeviceState, DeviceStates); itkGetMacro(ServiceProperties, us::ServiceProperties); void GrabImage(); /** * \brief Returns all probes for this device or an empty vector it no probes were set * Returns a std::vector of all probes that exist for this device if there were probes set while creating or modifying this USVideoDevice. * Otherwise it returns an empty vector. Therefore always check if vector is filled, before using it! */ virtual std::vector GetAllProbes() = 0; /** * \brief Cleans the std::vector containing all configured probes. */ virtual void DeleteAllProbes() {}; /** * \brief Return current active probe for this USDevice * Returns a pointer to the probe that is currently in use. If there were probes set while creating or modifying this USDevice. * Returns null otherwise */ virtual mitk::USProbe::Pointer GetCurrentProbe() = 0; /** \brief adds a new probe to the device */ virtual void AddNewProbe(mitk::USProbe::Pointer /*probe*/) {}; /** * \brief get the probe by its name * Returns a pointer to the probe identified by the given name. If no probe of given name exists for this Device 0 is returned. */ virtual mitk::USProbe::Pointer GetProbeByName(std::string name) = 0; /** * \brief Removes the Probe with the given name */ virtual void RemoveProbeByName(std::string /*name*/) {}; /** * \brief Sets the first existing probe or the default probe of the ultrasound device * as the current probe of it. */ virtual void SetDefaultProbeAsCurrentProbe() {}; /** * \brief Sets the probe with the given name as current probe if the named probe exists. */ virtual void SetCurrentProbe(std::string /*probename*/) {}; virtual void SetSpacing(double xSpacing, double ySpacing); protected: // Threading-Related - itk::ConditionVariable::Pointer m_FreezeBarrier; - itk::SimpleMutexLock m_FreezeMutex; - itk::MultiThreader::Pointer m_MultiThreader; ///< itk::MultiThreader used for thread handling - itk::FastMutexLock::Pointer m_ImageMutex; ///< mutex for images provided by the image source - int m_ThreadID; ///< ID of the started thread + std::condition_variable m_FreezeBarrier; + std::mutex m_FreezeMutex; + std::mutex m_ImageMutex; ///< mutex for images provided by the image source + std::thread m_Thread; virtual void SetImageVector(std::vector vec) { if (this->m_ImageVector != vec) { this->m_ImageVector = vec; this->Modified(); } } - static ITK_THREAD_RETURN_TYPE Acquire(void* pInfoStruct); - static ITK_THREAD_RETURN_TYPE ConnectThread(void* pInfoStruct); + void Acquire(); + void ConnectThread(); std::vector m_ImageVector; // Variables to determine if spacing was calibrated and needs to be applied to the incoming images mitk::Vector3D m_Spacing; /** * \brief Registers an OpenIGTLink device as a microservice so that we can send the images of * this device via the network. */ void ProvideViaOIGTL(); /** * \brief Deregisters the microservices for OpenIGTLink. */ void DisableOIGTL(); mitk::IGTLServer::Pointer m_IGTLServer; mitk::IGTLMessageProvider::Pointer m_IGTLMessageProvider; mitk::ImageToIGTLMessageFilter::Pointer m_ImageToIGTLMsgFilter; bool m_IsFreezed; DeviceStates m_DeviceState; /* @brief defines the area that should be cropped from the US image */ USImageCropArea m_CropArea; /** * \brief This Method constructs the service properties which can later be used to * register the object with the Microservices * Return service properties */ us::ServiceProperties ConstructServiceProperties(); /** * \brief Remove this device from the micro service. */ void UnregisterOnService(); /** * \brief Is called during the initialization process. * Override this method in a subclass to handle the actual initialization. * If it returns false, the initialization process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnInitialization() = 0; /** * \brief Is called during the connection process. * Override this method in a subclass to handle the actual connection. * If it returns false, the connection process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnConnection() = 0; /** * \brief Is called during the disconnection process. * Override this method in a subclass to handle the actual disconnection. * If it returns false, the disconnection process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnDisconnection() = 0; /** * \brief Is called during the activation process. * After this method is finished, the device should be generating images. * If it returns false, the activation process will be canceled. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnActivation() = 0; /** * \brief Is called during the deactivation process. * After a call to this method the device should still be connected, * but not producing images anymore. * * \return true if successful and false if unsuccessful * \throw mitk::Exception implementation may throw an exception to clarify what went wrong */ virtual bool OnDeactivation() = 0; /** * \brief Called when mitk::USDevice::SetIsFreezed() is called. * Subclasses can overwrite this method to do additional actions. Default * implementation does noting. */ virtual void OnFreeze(bool) { } /** * \brief Enforces minimal Metadata to be set. */ USDevice(std::string manufacturer, std::string model); /** * \brief Constructs a device with the given Metadata. Make sure the Metadata contains meaningful content! * \deprecated Use USDevice(std::string manufacturer, std::string model) instead. */ USDevice(mitk::USImageMetadata::Pointer metadata); ~USDevice() override; /** * \brief Grabs the next frame from the Video input. * This method is called internally, whenever Update() is invoked by an Output. */ void GenerateData() override; std::string GetServicePropertyLabel(); unsigned int m_NumberOfOutputs; /** * \brief Properties of the device's Microservice. */ us::ServiceProperties m_ServiceProperties; /** * \brief The device's ServiceRegistration object that allows to modify it's Microservice registraton details. */ us::ServiceRegistration m_ServiceRegistration; private: std::string m_Manufacturer; std::string m_Name; std::string m_Comment; bool m_SpawnAcquireThread; bool m_UnregisteringStarted; }; } // namespace mitk // This is the microservice declaration. Do not meddle! MITK_DECLARE_SERVICE_INTERFACE(mitk::USDevice, "org.mitk.services.UltrasoundDevice") #endif // MITKUSDevice_H_HEADER_INCLUDED_ diff --git a/Modules/US/USModel/mitkUSIGTLDevice.cpp b/Modules/US/USModel/mitkUSIGTLDevice.cpp index 33713b7c31..09c6afb003 100644 --- a/Modules/US/USModel/mitkUSIGTLDevice.cpp +++ b/Modules/US/USModel/mitkUSIGTLDevice.cpp @@ -1,207 +1,207 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include mitk::USIGTLDevice::USIGTLDevice(std::string manufacturer, std::string model, std::string host, int port, bool server) : mitk::USDevice(manufacturer, model), m_Host(host), m_Port(port) { m_ControlInterfaceCustom = mitk::USVideoDeviceCustomControls::New(this); if (server) { m_Device = mitk::IGTLServer::New(true); } else { m_Device = mitk::IGTLClient::New(true); } m_Device->SetPortNumber(m_Port); m_Device->SetHostname(m_Host); m_Device->SetName(manufacturer + " - " + model); m_TransformDeviceSource = mitk::IGTLTrackingDataDeviceSource::New(); m_TransformDeviceSource->SetIGTLDevice(m_Device); m_TransformDeviceSource->RegisterAsMicroservice(); m_DeviceSource = mitk::IGTL2DImageDeviceSource::New(); m_DeviceSource->SetIGTLDevice(m_Device); m_DeviceSource->RegisterAsMicroservice(); m_Filter = mitk::IGTLMessageToUSImageFilter::New(); m_Filter->SetNumberOfExpectedOutputs(1); m_Filter->ConnectTo(m_DeviceSource); } std::string mitk::USIGTLDevice::GetDeviceClass() { return "IGTL Client"; } mitk::USImageSource::Pointer mitk::USIGTLDevice::GetUSImageSource() { return m_Filter.GetPointer(); } mitk::USAbstractControlInterface::Pointer mitk::USIGTLDevice::GetControlInterfaceCustom() { return m_ControlInterfaceCustom.GetPointer(); } void mitk::USIGTLDevice::UnregisterOnService() { m_DeviceSource->UnRegisterMicroservice(); m_TransformDeviceSource->UnRegisterMicroservice(); mitk::USDevice::UnregisterOnService(); } std::vector mitk::USIGTLDevice::GetAllProbes() { if (m_Probes.empty()) { MITK_INFO << "No probes exist for this USVideDevice. Empty vector is returned"; } return m_Probes; } void mitk::USIGTLDevice::DeleteAllProbes() { m_Probes.clear(); } mitk::USProbe::Pointer mitk::USIGTLDevice::GetCurrentProbe() { if (m_CurrentProbe.IsNotNull()) { return m_CurrentProbe; } else { return nullptr; } } void mitk::USIGTLDevice::AddNewProbe(mitk::USProbe::Pointer probe) { m_Probes.push_back(probe); } mitk::USProbe::Pointer mitk::USIGTLDevice::GetProbeByName(std::string name) { for (std::vector::iterator it = m_Probes.begin(); it != m_Probes.end(); it++) { if (name.compare((*it)->GetName()) == 0) return (*it); } MITK_INFO << "No probe with given name " << name << " was found."; return nullptr; //no matching probe was found so 0 is returned } void mitk::USIGTLDevice::RemoveProbeByName(std::string name) { for (std::vector::iterator it = m_Probes.begin(); it != m_Probes.end(); it++) { if (name.compare((*it)->GetName()) == 0) { m_Probes.erase(it); return; } } MITK_INFO << "No Probe with given name " << name << " was found"; } void mitk::USIGTLDevice::SetDefaultProbeAsCurrentProbe() { if (m_Probes.size() == 0) { std::string name = "default"; mitk::USProbe::Pointer defaultProbe = mitk::USProbe::New(name); m_Probes.push_back(defaultProbe); } m_CurrentProbe = m_Probes.at(0); MITK_INFO << "SetDefaultProbeAsCurrentProbe()"; this->ProbeChanged(m_CurrentProbe->GetName()); } void mitk::USIGTLDevice::SetCurrentProbe(std::string probename) { m_CurrentProbe = this->GetProbeByName(probename); MITK_INFO << "SetCurrentProbe() " << probename; } void mitk::USIGTLDevice::SetSpacing(double xSpacing, double ySpacing) { mitk::Vector3D spacing; spacing[0] = xSpacing; spacing[1] = ySpacing; spacing[2] = 1; MITK_INFO << "Spacing: " << spacing; if (m_CurrentProbe.IsNotNull()) { m_CurrentProbe->SetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth(), spacing); } else { MITK_WARN << "Cannot set spacing. Current ultrasound probe not set."; } } bool mitk::USIGTLDevice::OnInitialization() { return true; } bool mitk::USIGTLDevice::OnConnection() { if (m_Device->GetState() == mitk::IGTLDevice::IGTLDeviceState::Running || m_Device->GetState() == mitk::IGTLDevice::IGTLDeviceState::Ready) { MITK_INFO << "Device is ready or running. So return true"; return true; } return m_Device->OpenConnection(); } bool mitk::USIGTLDevice::OnDisconnection() { return m_Device->CloseConnection(); } bool mitk::USIGTLDevice::OnActivation() { if (m_Device->GetState() == mitk::IGTLDevice::IGTLDeviceState::Running ) { MITK_INFO << "Device is running. So return true"; return true; } return m_Device->StartCommunication(); } bool mitk::USIGTLDevice::OnDeactivation() { return m_Device->StopCommunication(); } void mitk::USIGTLDevice::GenerateData() { Superclass::GenerateData(); if (m_ImageVector.size() == 0 || this->GetNumberOfIndexedOutputs() == 0) { return; } - m_ImageMutex->Lock(); + m_ImageMutex.lock(); auto& image = m_ImageVector[0]; if (image.IsNotNull() && image->IsInitialized() && m_CurrentProbe.IsNotNull()) { //MITK_INFO << "Spacing CurrentProbe: " << m_CurrentProbe->GetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth()); image->GetGeometry()->SetSpacing(m_CurrentProbe->GetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth())); this->GetOutput(0)->SetGeometry(image->GetGeometry()); } - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); } diff --git a/Modules/US/USModel/mitkUSVideoDevice.cpp b/Modules/US/USModel/mitkUSVideoDevice.cpp index 8f849a4bb0..da7617282a 100644 --- a/Modules/US/USModel/mitkUSVideoDevice.cpp +++ b/Modules/US/USModel/mitkUSVideoDevice.cpp @@ -1,254 +1,254 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkUSVideoDevice.h" #include "mitkUSVideoDeviceCustomControls.h" mitk::USVideoDevice::USVideoDevice(int videoDeviceNumber, std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model) { Init(); m_SourceIsFile = false; m_DeviceID = videoDeviceNumber; m_FilePath = ""; } mitk::USVideoDevice::USVideoDevice(std::string videoFilePath, std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model) { Init(); m_SourceIsFile = true; m_FilePath = videoFilePath; } mitk::USVideoDevice::USVideoDevice(int videoDeviceNumber, mitk::USImageMetadata::Pointer metadata) : mitk::USDevice(metadata) { Init(); m_SourceIsFile = false; m_DeviceID = videoDeviceNumber; m_FilePath = ""; } mitk::USVideoDevice::USVideoDevice(std::string videoFilePath, mitk::USImageMetadata::Pointer metadata) : mitk::USDevice(metadata) { Init(); m_SourceIsFile = true; m_FilePath = videoFilePath; } mitk::USVideoDevice::~USVideoDevice() { //m_Source->UnRegister(); m_Source = nullptr; } void mitk::USVideoDevice::Init() { m_Source = mitk::USImageVideoSource::New(); m_ControlInterfaceCustom = mitk::USVideoDeviceCustomControls::New(this); //this->SetNumberOfInputs(1); this->SetNumberOfIndexedOutputs(1); // mitk::USImage::Pointer output = mitk::USImage::New(); // output->Initialize(); this->SetNthOutput(0, this->MakeOutput(0)); } std::string mitk::USVideoDevice::GetDeviceClass() { return mitk::USVideoDevice::GetDeviceClassStatic(); } std::string mitk::USVideoDevice::GetDeviceClassStatic() { return "org.mitk.modules.us.USVideoDevice"; } mitk::USAbstractControlInterface::Pointer mitk::USVideoDevice::GetControlInterfaceCustom() { return m_ControlInterfaceCustom.GetPointer(); } bool mitk::USVideoDevice::OnInitialization() { // nothing to do at initialization of video device return true; } bool mitk::USVideoDevice::OnConnection() { if (m_SourceIsFile){ m_Source->SetVideoFileInput(m_FilePath); } else { m_Source->SetCameraInput(m_DeviceID); } //SetSourceCropArea(); return true; } bool mitk::USVideoDevice::OnDisconnection() { if (m_DeviceState == State_Activated) this->Deactivate(); m_Source->ReleaseInput(); return true; } bool mitk::USVideoDevice::OnActivation() { // make sure that video device is ready before aquiring images if (!m_Source->GetIsReady()) { MITK_WARN("mitkUSDevice")("mitkUSVideoDevice") << "Could not activate us video device. Check if video grabber is configured correctly."; return false; } MITK_INFO << "Activated UsVideoDevice!"; return true; } bool mitk::USVideoDevice::OnDeactivation() { // happens automatically when m_Active is set to false return true; } void mitk::USVideoDevice::GenerateData() { Superclass::GenerateData(); if( m_ImageVector.size() == 0 || this->GetNumberOfIndexedOutputs() == 0 ) { return; } - m_ImageMutex->Lock(); + m_ImageMutex.lock(); auto& image = m_ImageVector[0]; if( image.IsNotNull() && image->IsInitialized() && m_CurrentProbe.IsNotNull() ) { //MITK_INFO << "Spacing CurrentProbe: " << m_CurrentProbe->GetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth()); image->GetGeometry()->SetSpacing(m_CurrentProbe->GetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth())); this->GetOutput(0)->SetGeometry(image->GetGeometry()); } - m_ImageMutex->Unlock(); + m_ImageMutex.unlock(); } void mitk::USVideoDevice::UnregisterOnService() { if (m_DeviceState == State_Activated) { this->Deactivate(); } if (m_DeviceState == State_Connected) { this->Disconnect(); } mitk::USDevice::UnregisterOnService(); } mitk::USImageSource::Pointer mitk::USVideoDevice::GetUSImageSource() { return m_Source.GetPointer(); } std::vector mitk::USVideoDevice::GetAllProbes() { if (m_Probes.empty()) { MITK_INFO << "No probes exist for this USVideDevice. Empty vector is returned"; } return m_Probes; } void mitk::USVideoDevice::DeleteAllProbes() { m_Probes.clear(); } mitk::USProbe::Pointer mitk::USVideoDevice::GetCurrentProbe() { if (m_CurrentProbe.IsNotNull()) { return m_CurrentProbe; } else { return nullptr; } } mitk::USProbe::Pointer mitk::USVideoDevice::GetProbeByName(std::string name) { for (std::vector::iterator it = m_Probes.begin(); it != m_Probes.end(); it++) { if (name.compare((*it)->GetName()) == 0) return (*it); } MITK_INFO << "No probe with given name " << name << " was found."; return nullptr; //no matching probe was found so 0 is returned } void mitk::USVideoDevice::RemoveProbeByName(std::string name) { for (std::vector::iterator it = m_Probes.begin(); it != m_Probes.end(); it++) { if (name.compare((*it)->GetName()) == 0) { m_Probes.erase(it); return; } } MITK_INFO << "No Probe with given name " << name << " was found"; } void mitk::USVideoDevice::AddNewProbe(mitk::USProbe::Pointer probe) { m_Probes.push_back(probe); } bool mitk::USVideoDevice::GetIsSourceFile() { return m_SourceIsFile; } void mitk::USVideoDevice::SetDefaultProbeAsCurrentProbe() { if( m_Probes.size() == 0 ) { std::string name = "default"; mitk::USProbe::Pointer defaultProbe = mitk::USProbe::New( name ); m_Probes.push_back( defaultProbe ); } m_CurrentProbe = m_Probes.at(0); MITK_INFO << "SetDefaultProbeAsCurrentProbe()"; this->ProbeChanged( m_CurrentProbe->GetName() ); } void mitk::USVideoDevice::SetCurrentProbe(std::string probename) { m_CurrentProbe = this->GetProbeByName( probename ); MITK_INFO << "SetCurrentProbe() " << probename; } void mitk::USVideoDevice::SetSpacing(double xSpacing, double ySpacing) { mitk::Vector3D spacing; spacing[0] = xSpacing; spacing[1] = ySpacing; spacing[2] = 1; MITK_INFO << "Spacing: " << spacing; if( m_CurrentProbe.IsNotNull() ) { m_CurrentProbe->SetSpacingForGivenDepth(m_CurrentProbe->GetCurrentDepth(), spacing); } else { MITK_WARN << "Cannot set spacing. Current ultrasound probe not set."; } } diff --git a/Plugins/org.mitk.gui.qt.basicimageprocessing/CMakeLists.txt b/Plugins/org.mitk.gui.qt.basicimageprocessing/CMakeLists.txt index a6f5a3fb89..a7de496eac 100644 --- a/Plugins/org.mitk.gui.qt.basicimageprocessing/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.basicimageprocessing/CMakeLists.txt @@ -1,7 +1,8 @@ project(org_mitk_gui_qt_basicimageprocessing) mitk_create_plugin( EXPORT_DIRECTIVE BASICIMAGEPROCESSING_EXPORT EXPORTED_INCLUDE_SUFFIXES src MODULE_DEPENDS MitkQtWidgetsExt MitkMapperExt MitkImageDenoising + PACKAGE_DEPENDS ITK|MathematicalMorphology+Smoothing ) diff --git a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/CMakeLists.txt b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/CMakeLists.txt index b5c7ec2ed4..f7c18446e3 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/CMakeLists.txt @@ -1,16 +1,16 @@ project(org_mitk_gui_qt_igt_app_ultrasoundtrackingnavigation) mitk_create_plugin( EXPORT_DIRECTIVE IGTAPPUSTRACKINGNAVIGATION_EXPORT EXPORTED_INCLUDE_SUFFIXES src - PACKAGE_DEPENDS PRIVATE CTK ITK|LabelMap Poco + PACKAGE_DEPENDS PRIVATE CTK ITK|LabelMap+Smoothing Poco MODULE_DEPENDS MitkUSUI MitkUSNavigation MitkIGTUI MitkSceneSerialization MitkContourModel ) #usFunctionAddResources(TARGET ${PLUGIN_TARGET} # MODULE_NAME liborg_mitk_gui_qt_usnavigation # WORKING_DIRECTORY resources # FILES Interactions/USPointMarkInteractions.xml # Interactions/USZoneInteractions.xml # Interactions/USZoneInteractionsHold.xml #) diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp index eaed8a6bcc..4fb59d9e64 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp @@ -1,503 +1,503 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkImageCropperView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkImageCropperView::VIEW_ID = "org.mitk.views.qmitkimagecropper"; QmitkImageCropperView::QmitkImageCropperView(QObject *) : m_ParentWidget(nullptr) , m_BoundingShapeInteractor(nullptr) , m_CropOutsideValue(0) { CreateBoundingShapeInteractor(false); } QmitkImageCropperView::~QmitkImageCropperView() { //disable interactor if (m_BoundingShapeInteractor != nullptr) { m_BoundingShapeInteractor->SetDataNode(nullptr); m_BoundingShapeInteractor->EnableInteraction(false); } } void QmitkImageCropperView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Controls.imageSelectionWidget->SetDataStorage(GetDataStorage()); m_Controls.imageSelectionWidget->SetNodePredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); m_Controls.imageSelectionWidget->SetSelectionIsOptional(true); m_Controls.imageSelectionWidget->SetAutoSelectNewNodes(true); m_Controls.imageSelectionWidget->SetEmptyInfo(QString("Please select an image node")); m_Controls.imageSelectionWidget->SetPopUpTitel(QString("Select image node")); connect(m_Controls.imageSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkImageCropperView::OnImageSelectionChanged); m_Controls.boundingBoxSelectionWidget->SetDataStorage(GetDataStorage()); m_Controls.boundingBoxSelectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); m_Controls.boundingBoxSelectionWidget->SetSelectionIsOptional(true); m_Controls.boundingBoxSelectionWidget->SetAutoSelectNewNodes(true); m_Controls.boundingBoxSelectionWidget->SetEmptyInfo(QString("Please select a bounding box")); m_Controls.boundingBoxSelectionWidget->SetPopUpTitel(QString("Select bounding box node")); connect(m_Controls.boundingBoxSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkImageCropperView::OnBoundingBoxSelectionChanged); connect(m_Controls.buttonCreateNewBoundingBox, SIGNAL(clicked()), this, SLOT(OnCreateNewBoundingBox())); connect(m_Controls.buttonCropping, SIGNAL(clicked()), this, SLOT(OnCropping())); connect(m_Controls.buttonMasking, SIGNAL(clicked()), this, SLOT(OnMasking())); auto lambda = [this]() { m_Controls.groupImageSettings->setVisible(!m_Controls.groupImageSettings->isVisible()); }; connect(m_Controls.buttonAdvancedSettings, &ctkExpandButton::clicked, this, lambda); connect(m_Controls.spinBoxOutsidePixelValue, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueChanged(int))); SetDefaultGUI(); m_ParentWidget = parent; this->OnImageSelectionChanged(m_Controls.imageSelectionWidget->GetSelectedNodes()); this->OnBoundingBoxSelectionChanged(m_Controls.boundingBoxSelectionWidget->GetSelectedNodes()); } void QmitkImageCropperView::OnImageSelectionChanged(QList) { bool rotationEnabled = false; auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { SetDefaultGUI(); return; } auto image = dynamic_cast(imageNode->GetData()); if (nullptr != image) { if (image->GetDimension() < 3) { QMessageBox::warning(nullptr, tr("Invalid image selected"), tr("ImageCropper only works with 3 or more dimensions."), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); SetDefaultGUI(); return; } m_ParentWidget->setEnabled(true); m_Controls.buttonCreateNewBoundingBox->setEnabled(true); vtkSmartPointer imageMat = image->GetGeometry()->GetVtkMatrix(); // check whether the image geometry is rotated; if so, no pixel aligned cropping or masking can be performed if ((imageMat->GetElement(1, 0) == 0.0) && (imageMat->GetElement(0, 1) == 0.0) && (imageMat->GetElement(1, 2) == 0.0) && (imageMat->GetElement(2, 1) == 0.0) && (imageMat->GetElement(2, 0) == 0.0) && (imageMat->GetElement(0, 2) == 0.0)) { rotationEnabled = false; m_Controls.labelWarningRotation->setVisible(false); } else { rotationEnabled = true; m_Controls.labelWarningRotation->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); m_Controls.labelWarningRotation->setVisible(true); } this->CreateBoundingShapeInteractor(rotationEnabled); - if (itk::ImageIOBase::SCALAR == image->GetPixelType().GetPixelType()) + if (itk::IOPixelEnum::SCALAR == image->GetPixelType().GetPixelType()) { // Might be changed with the upcoming new image statistics plugin //(recomputation might be very expensive for large images ;) ) auto statistics = image->GetStatistics(); auto minPixelValue = statistics->GetScalarValueMin(); auto maxPixelValue = statistics->GetScalarValueMax(); if (minPixelValue < std::numeric_limits::min()) { minPixelValue = std::numeric_limits::min(); } if (maxPixelValue > std::numeric_limits::max()) { maxPixelValue = std::numeric_limits::max(); } m_Controls.spinBoxOutsidePixelValue->setEnabled(true); m_Controls.spinBoxOutsidePixelValue->setMaximum(static_cast(maxPixelValue)); m_Controls.spinBoxOutsidePixelValue->setMinimum(static_cast(minPixelValue)); m_Controls.spinBoxOutsidePixelValue->setValue(static_cast(minPixelValue)); } else { m_Controls.spinBoxOutsidePixelValue->setEnabled(false); } unsigned int dim = image->GetDimension(); if (dim < 2 || dim > 4) { m_ParentWidget->setEnabled(false); } if (m_Controls.boundingBoxSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCropping->setEnabled(true); m_Controls.buttonMasking->setEnabled(true); m_Controls.buttonAdvancedSettings->setEnabled(true); m_Controls.groupImageSettings->setEnabled(true); } } } void QmitkImageCropperView::OnBoundingBoxSelectionChanged(QList) { auto boundingBoxNode = m_Controls.boundingBoxSelectionWidget->GetSelectedNode(); if (boundingBoxNode.IsNull()) { SetDefaultGUI(); m_BoundingShapeInteractor->EnableInteraction(false); m_BoundingShapeInteractor->SetDataNode(nullptr); if (m_Controls.imageSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCreateNewBoundingBox->setEnabled(true); } return; } auto boundingBox = dynamic_cast(boundingBoxNode->GetData()); if (nullptr != boundingBox) { // node newly selected boundingBoxNode->SetVisibility(true); m_BoundingShapeInteractor->EnableInteraction(true); m_BoundingShapeInteractor->SetDataNode(boundingBoxNode); mitk::RenderingManager::GetInstance()->InitializeViews(); if (m_Controls.imageSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCropping->setEnabled(true); m_Controls.buttonMasking->setEnabled(true); m_Controls.buttonAdvancedSettings->setEnabled(true); m_Controls.groupImageSettings->setEnabled(true); } } } void QmitkImageCropperView::OnCreateNewBoundingBox() { auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { return; } if (nullptr == imageNode->GetData()) { return; } QString name = QString::fromStdString(imageNode->GetName() + " Bounding Shape"); auto boundingShape = this->GetDataStorage()->GetNode(mitk::NodePredicateFunction::New([&name](const mitk::DataNode *node) { return 0 == node->GetName().compare(name.toStdString()); })); if (nullptr != boundingShape) { name = this->AdaptBoundingObjectName(name); } // get current timestep to support 3d+t images auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); const auto timePoint = renderWindowPart->GetSelectedTimePoint(); const auto imageGeometry = imageNode->GetData()->GetTimeGeometry()->GetGeometryForTimePoint(timePoint); auto boundingBox = mitk::GeometryData::New(); boundingBox->SetGeometry(static_cast(this->InitializeWithImageGeometry(imageGeometry))); auto boundingBoxNode = mitk::DataNode::New(); boundingBoxNode->SetData(boundingBox); boundingBoxNode->SetProperty("name", mitk::StringProperty::New(name.toStdString())); boundingBoxNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); boundingBoxNode->SetProperty("opacity", mitk::FloatProperty::New(0.6)); boundingBoxNode->SetProperty("layer", mitk::IntProperty::New(99)); boundingBoxNode->AddProperty("handle size factor", mitk::DoubleProperty::New(1.0 / 40.0)); boundingBoxNode->SetBoolProperty("pickable", true); if (!this->GetDataStorage()->Exists(boundingBoxNode)) { GetDataStorage()->Add(boundingBoxNode, imageNode); } m_Controls.boundingBoxSelectionWidget->SetCurrentSelectedNode(boundingBoxNode); } void QmitkImageCropperView::OnCropping() { this->ProcessImage(false); } void QmitkImageCropperView::OnMasking() { this->ProcessImage(true); } void QmitkImageCropperView::OnSliderValueChanged(int slidervalue) { m_CropOutsideValue = slidervalue; } void QmitkImageCropperView::CreateBoundingShapeInteractor(bool rotationEnabled) { if (m_BoundingShapeInteractor.IsNull()) { m_BoundingShapeInteractor = mitk::BoundingShapeInteractor::New(); m_BoundingShapeInteractor->LoadStateMachine("BoundingShapeInteraction.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); m_BoundingShapeInteractor->SetEventConfig("BoundingShapeMouseConfig.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); } m_BoundingShapeInteractor->SetRotationEnabled(rotationEnabled); } mitk::Geometry3D::Pointer QmitkImageCropperView::InitializeWithImageGeometry(const mitk::BaseGeometry* geometry) const { // convert a BaseGeometry into a Geometry3D (otherwise IO is not working properly) if (geometry == nullptr) mitkThrow() << "Geometry is not valid."; auto boundingGeometry = mitk::Geometry3D::New(); boundingGeometry->SetBounds(geometry->GetBounds()); boundingGeometry->SetImageGeometry(geometry->GetImageGeometry()); boundingGeometry->SetOrigin(geometry->GetOrigin()); boundingGeometry->SetSpacing(geometry->GetSpacing()); boundingGeometry->SetIndexToWorldTransform(geometry->GetIndexToWorldTransform()->Clone()); boundingGeometry->Modified(); return boundingGeometry; } void QmitkImageCropperView::ProcessImage(bool mask) { auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); const auto timePoint = renderWindowPart->GetSelectedTimePoint(); auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); return; } auto boundingBoxNode = m_Controls.boundingBoxSelectionWidget->GetSelectedNode(); if (boundingBoxNode.IsNull()) { QMessageBox::information(nullptr, "Warning", "Please load and select a cropping object before starting image processing."); return; } if (!imageNode->GetData()->GetTimeGeometry()->IsValidTimePoint(timePoint)) { QMessageBox::information(nullptr, "Warning", "Please select a time point that is within the time bounds of the selected image."); return; } const auto timeStep = imageNode->GetData()->GetTimeGeometry()->TimePointToTimeStep(timePoint); auto image = dynamic_cast(imageNode->GetData()); auto boundingBox = dynamic_cast(boundingBoxNode->GetData()); if (nullptr != image && nullptr != boundingBox) { QString imageName; if (mask) { imageName = QString::fromStdString(imageNode->GetName() + "_" + boundingBoxNode->GetName() + "_masked"); } else { imageName = QString::fromStdString(imageNode->GetName() + "_" + boundingBoxNode->GetName() + "_cropped"); } if (m_Controls.checkBoxCropTimeStepOnly->isChecked()) { imageName = imageName + "_T" + QString::number(timeStep); } // image and bounding shape ok, set as input auto croppedImageNode = mitk::DataNode::New(); auto cutter = mitk::BoundingShapeCropper::New(); cutter->SetGeometry(boundingBox); // adjustable in advanced settings cutter->SetUseWholeInputRegion(mask); //either mask (mask=true) or crop (mask=false) cutter->SetOutsideValue(m_CropOutsideValue); cutter->SetUseCropTimeStepOnly(m_Controls.checkBoxCropTimeStepOnly->isChecked()); cutter->SetCurrentTimeStep(timeStep); // TODO: Add support for MultiLayer (right now only Mulitlabel support) auto labelsetImageInput = dynamic_cast(image); if (nullptr != labelsetImageInput) { cutter->SetInput(labelsetImageInput); // do the actual cutting try { cutter->Update(); } catch (const itk::ExceptionObject& e) { std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); return; } auto labelSetImage = mitk::LabelSetImage::New(); labelSetImage->InitializeByLabeledImage(cutter->GetOutput()); for (unsigned int i = 0; i < labelsetImageInput->GetNumberOfLayers(); i++) { labelSetImage->AddLabelSetToLayer(i, labelsetImageInput->GetLabelSet(i)); } croppedImageNode->SetData(labelSetImage); croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); //add cropping result to the current data storage as child node to the image node if (!m_Controls.checkOverwriteImage->isChecked()) { if (!this->GetDataStorage()->Exists(croppedImageNode)) { this->GetDataStorage()->Add(croppedImageNode, imageNode); } } else // original image will be overwritten by the result image and the bounding box of the result is adjusted { imageNode->SetData(labelSetImage); imageNode->Modified(); // Adjust coordinate system by doing a reinit on auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); tempDataStorage->InsertElement(0, imageNode); // initialize the views to the bounding geometry auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); mitk::RenderingManager::GetInstance()->InitializeViews(bounds); } } else { cutter->SetInput(image); // do the actual cutting try { cutter->Update(); } catch (const itk::ExceptionObject& e) { std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); return; } //add cropping result to the current data storage as child node to the image node if (!m_Controls.checkOverwriteImage->isChecked()) { croppedImageNode->SetData(cutter->GetOutput()); croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); croppedImageNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); mitk::LevelWindow levelWindow; imageNode->GetLevelWindow(levelWindow); croppedImageNode->SetLevelWindow(levelWindow); if (!this->GetDataStorage()->Exists(croppedImageNode)) { this->GetDataStorage()->Add(croppedImageNode, imageNode); imageNode->SetVisibility(mask); // Give the user a visual clue that something happened when image was cropped } } else // original image will be overwritten by the result image and the bounding box of the result is adjusted { mitk::LevelWindow levelWindow; imageNode->GetLevelWindow(levelWindow); imageNode->SetData(cutter->GetOutput()); imageNode->SetLevelWindow(levelWindow); // Adjust coordinate system by doing a reinit on auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); tempDataStorage->InsertElement(0, imageNode); // initialize the views to the bounding geometry auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); mitk::RenderingManager::GetInstance()->InitializeViews(bounds); } } } else { QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); } } void QmitkImageCropperView::SetDefaultGUI() { m_Controls.labelWarningRotation->setVisible(false); m_Controls.buttonCreateNewBoundingBox->setEnabled(false); m_Controls.buttonCropping->setEnabled(false); m_Controls.buttonMasking->setEnabled(false); m_Controls.buttonAdvancedSettings->setEnabled(false); m_Controls.groupImageSettings->setEnabled(false); m_Controls.groupImageSettings->setVisible(false); m_Controls.checkOverwriteImage->setChecked(false); m_Controls.checkBoxCropTimeStepOnly->setChecked(false); } QString QmitkImageCropperView::AdaptBoundingObjectName(const QString& name) const { unsigned int counter = 2; QString newName = QString("%1 %2").arg(name).arg(counter); while (nullptr != this->GetDataStorage()->GetNode(mitk::NodePredicateFunction::New([&newName](const mitk::DataNode *node) { return 0 == node->GetName().compare(newName.toStdString()); }))) { newName = QString("%1 %2").arg(name).arg(++counter); } return newName; } diff --git a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp index 76bc6fcd53..9aa912fb4a 100644 --- a/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp +++ b/Plugins/org.mitk.gui.qt.imagenavigator/src/internal/QmitkImageNavigatorView.cpp @@ -1,612 +1,612 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "QmitkImageNavigatorView.h" #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkImageNavigatorView::VIEW_ID = "org.mitk.views.imagenavigator"; QmitkImageNavigatorView::QmitkImageNavigatorView() : m_AxialStepper(nullptr) , m_SagittalStepper(nullptr) , m_FrontalStepper(nullptr) , m_TimeStepper(nullptr) , m_Parent(nullptr) , m_IRenderWindowPart(nullptr) { } QmitkImageNavigatorView::~QmitkImageNavigatorView() { } void QmitkImageNavigatorView::CreateQtPartControl(QWidget *parent) { // create GUI widgets m_Parent = parent; m_Controls.setupUi(parent); connect(m_Controls.m_XWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_YWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); connect(m_Controls.m_ZWorldCoordinateSpinBox, SIGNAL(valueChanged(double)), this, SLOT(OnMillimetreCoordinateValueChanged())); m_Parent->setEnabled(false); mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); this->RenderWindowPartActivated(renderPart); } void QmitkImageNavigatorView::SetFocus () { m_Controls.m_XWorldCoordinateSpinBox->setFocus(); } void QmitkImageNavigatorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (this->m_IRenderWindowPart != renderWindowPart) { this->m_IRenderWindowPart = renderWindowPart; this->m_Parent->setEnabled(true); QmitkRenderWindow* renderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { if (m_AxialStepper) m_AxialStepper->deleteLater(); m_AxialStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorAxial, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorAxialFromSimpleExample"); m_Controls.m_SliceNavigatorAxial->setEnabled(true); m_Controls.m_AxialLabel->setEnabled(true); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(true); connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorAxial->setEnabled(false); m_Controls.m_AxialLabel->setEnabled(false); m_Controls.m_ZWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { if (m_SagittalStepper) m_SagittalStepper->deleteLater(); m_SagittalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorSagittal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorSagittalFromSimpleExample"); m_Controls.m_SliceNavigatorSagittal->setEnabled(true); m_Controls.m_SagittalLabel->setEnabled(true); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(true); connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorSagittal->setEnabled(false); m_Controls.m_SagittalLabel->setEnabled(false); m_Controls.m_YWorldCoordinateSpinBox->setEnabled(false); } renderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { if (m_FrontalStepper) m_FrontalStepper->deleteLater(); m_FrontalStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorFrontal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorFrontalFromSimpleExample"); m_Controls.m_SliceNavigatorFrontal->setEnabled(true); m_Controls.m_CoronalLabel->setEnabled(true); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(true); connect(m_FrontalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); connect(m_FrontalStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorFrontal->setEnabled(false); m_Controls.m_CoronalLabel->setEnabled(false); m_Controls.m_XWorldCoordinateSpinBox->setEnabled(false); } mitk::SliceNavigationController* timeController = renderWindowPart->GetTimeNavigationController(); if (timeController) { if (m_TimeStepper) m_TimeStepper->deleteLater(); m_TimeStepper = new QmitkStepperAdapter(m_Controls.m_SliceNavigatorTime, timeController->GetTime(), "sliceNavigatorTimeFromSimpleExample"); m_Controls.m_SliceNavigatorTime->setEnabled(true); m_Controls.m_TimeLabel->setEnabled(true); connect(m_TimeStepper, SIGNAL(Refetch()), this, SLOT(UpdateStatusBar())); } else { m_Controls.m_SliceNavigatorTime->setEnabled(false); m_Controls.m_TimeLabel->setEnabled(false); } this->OnRefetch(); this->UpdateStatusBar(); } } void QmitkImageNavigatorView::UpdateStatusBar() { if (m_IRenderWindowPart != nullptr) { mitk::Point3D position = m_IRenderWindowPart->GetSelectedPosition(); mitk::BaseRenderer::Pointer baseRenderer = mitk::BaseRenderer::GetInstance(m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetVtkRenderWindow()); auto globalCurrentTimePoint = baseRenderer->GetTime(); mitk::TNodePredicateDataType::Pointer isImageData = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = GetDataStorage()->GetSubset(isImageData).GetPointer(); if (nodes.IsNotNull()) { mitk::Image::Pointer image3D; mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; int component = 0; node = mitk::FindTopmostVisibleNode(nodes, position, globalCurrentTimePoint, baseRenderer); if (node.IsNotNull()) { bool isBinary(false); node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = GetDataStorage()->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = mitk::FindTopmostVisibleNode(sourcenodes, position, globalCurrentTimePoint, baseRenderer); } if (topSourceNode.IsNotNull()) { image3D = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image3D = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } // get the position and pixel value from the image and build up status bar text auto statusBar = mitk::StatusBar::GetInstance(); if (image3D.IsNotNull() && statusBar != nullptr) { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(position, p); auto pixelType = image3D->GetChannelDescriptor().GetPixelType().GetPixelType(); - if (pixelType == itk::ImageIOBase::RGB || pixelType == itk::ImageIOBase::RGBA) + if (pixelType == itk::IOPixelEnum::RGB || pixelType == itk::IOPixelEnum::RGBA) { std::string pixelValue = "Pixel RGB(A) value: "; pixelValue.append(ConvertCompositePixelValueToString(image3D, p)); statusBar->DisplayImageInfo(position, p, globalCurrentTimePoint, pixelValue.c_str()); } - else if (pixelType == itk::ImageIOBase::DIFFUSIONTENSOR3D || pixelType == itk::ImageIOBase::SYMMETRICSECONDRANKTENSOR) + else if (pixelType == itk::IOPixelEnum::DIFFUSIONTENSOR3D || pixelType == itk::IOPixelEnum::SYMMETRICSECONDRANKTENSOR) { std::string pixelValue = "See ODF Details view. "; statusBar->DisplayImageInfo(position, p, globalCurrentTimePoint, pixelValue.c_str()); } else { itk::Index<3> p; image3D->GetGeometry()->WorldToIndex(position, p); mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5( mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, image3D->GetVolumeData(image3D->GetTimeGeometry()->TimePointToTimeStep(globalCurrentTimePoint)), p, pixelValue, component); statusBar->DisplayImageInfo(position, p, globalCurrentTimePoint, pixelValue); } } else { statusBar->DisplayImageInfoInvalid(); } } } } void QmitkImageNavigatorView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_IRenderWindowPart = nullptr; m_Parent->setEnabled(false); } int QmitkImageNavigatorView::GetSizeFlags(bool width) { if(!width) { return berry::Constants::MIN | berry::Constants::MAX | berry::Constants::FILL; } else { return 0; } } int QmitkImageNavigatorView::ComputePreferredSize(bool width, int /*availableParallel*/, int /*availablePerpendicular*/, int preferredResult) { if(width==false) { return 200; } else { return preferredResult; } } int QmitkImageNavigatorView::GetClosestAxisIndex(mitk::Vector3D normal) { // cos(theta) = normal . axis // cos(theta) = (a, b, c) . (d, e, f) // cos(theta) = (a, b, c) . (1, 0, 0) = a // cos(theta) = (a, b, c) . (0, 1, 0) = b // cos(theta) = (a, b, c) . (0, 0, 1) = c double absCosThetaWithAxis[3]; for (int i = 0; i < 3; i++) { absCosThetaWithAxis[i] = fabs(normal[i]); } int largestIndex = 0; double largestValue = absCosThetaWithAxis[0]; for (int i = 1; i < 3; i++) { if (absCosThetaWithAxis[i] > largestValue) { largestValue = absCosThetaWithAxis[i]; largestIndex = i; } } return largestIndex; } void QmitkImageNavigatorView::SetBorderColors() { if (m_IRenderWindowPart) { QString decoColor; QmitkRenderWindow* renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("sagittal"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } renderWindow = m_IRenderWindowPart->GetQmitkRenderWindow("coronal"); if (renderWindow) { decoColor = GetDecorationColorOfGeometry(renderWindow); mitk::PlaneGeometry::ConstPointer geometry = renderWindow->GetSliceNavigationController()->GetCurrentPlaneGeometry(); if (geometry.IsNotNull()) { mitk::Vector3D normal = geometry->GetNormal(); int axis = this->GetClosestAxisIndex(normal); this->SetBorderColor(axis, decoColor); } } } } QString QmitkImageNavigatorView::GetDecorationColorOfGeometry(QmitkRenderWindow* renderWindow) { QColor color; float rgb[3] = {1.0f, 1.0f, 1.0f}; float rgbMax = 255.0f; mitk::BaseRenderer::GetInstance(renderWindow->GetVtkRenderWindow())->GetCurrentWorldPlaneGeometryNode()->GetColor(rgb); color.setRed(static_cast(rgb[0]*rgbMax + 0.5)); color.setGreen(static_cast(rgb[1]*rgbMax + 0.5)); color.setBlue(static_cast(rgb[2]*rgbMax + 0.5)); QString colorAsString = QString(color.name()); return colorAsString; } void QmitkImageNavigatorView::SetBorderColor(int axis, QString colorAsStyleSheetString) { if (axis == 0) { this->SetBorderColor(m_Controls.m_XWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 1) { this->SetBorderColor(m_Controls.m_YWorldCoordinateSpinBox, colorAsStyleSheetString); } else if (axis == 2) { this->SetBorderColor(m_Controls.m_ZWorldCoordinateSpinBox, colorAsStyleSheetString); } } void QmitkImageNavigatorView::SetBorderColor(QDoubleSpinBox *spinBox, QString colorAsStyleSheetString) { assert(spinBox); spinBox->setStyleSheet(QString("border: 2px solid ") + colorAsStyleSheetString + ";"); } void QmitkImageNavigatorView::SetStepSizes() { this->SetStepSize(0); this->SetStepSize(1); this->SetStepSize(2); } void QmitkImageNavigatorView::SetStepSize(int axis) { if (m_IRenderWindowPart) { mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); if (geometry.IsNotNull()) { mitk::Point3D crossPositionInIndexCoordinates; mitk::Point3D crossPositionInIndexCoordinatesPlus1; mitk::Point3D crossPositionInMillimetresPlus1; mitk::Vector3D transformedAxisDirection; mitk::Point3D crossPositionInMillimetres = m_IRenderWindowPart->GetSelectedPosition(); geometry->WorldToIndex(crossPositionInMillimetres, crossPositionInIndexCoordinates); crossPositionInIndexCoordinatesPlus1 = crossPositionInIndexCoordinates; crossPositionInIndexCoordinatesPlus1[axis] += 1; geometry->IndexToWorld(crossPositionInIndexCoordinatesPlus1, crossPositionInMillimetresPlus1); transformedAxisDirection = crossPositionInMillimetresPlus1 - crossPositionInMillimetres; int closestAxisInMillimetreSpace = this->GetClosestAxisIndex(transformedAxisDirection); double stepSize = transformedAxisDirection.GetNorm(); this->SetStepSize(closestAxisInMillimetreSpace, stepSize); } } } void QmitkImageNavigatorView::SetStepSize(int axis, double stepSize) { if (axis == 0) { m_Controls.m_XWorldCoordinateSpinBox->setSingleStep(stepSize); } else if (axis == 1) { m_Controls.m_YWorldCoordinateSpinBox->setSingleStep(stepSize); } else if (axis == 2) { m_Controls.m_ZWorldCoordinateSpinBox->setSingleStep(stepSize); } } void QmitkImageNavigatorView::OnMillimetreCoordinateValueChanged() { if (m_IRenderWindowPart) { mitk::TimeGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNotNull()) { mitk::Point3D positionInWorldCoordinates; positionInWorldCoordinates[0] = m_Controls.m_XWorldCoordinateSpinBox->value(); positionInWorldCoordinates[1] = m_Controls.m_YWorldCoordinateSpinBox->value(); positionInWorldCoordinates[2] = m_Controls.m_ZWorldCoordinateSpinBox->value(); m_IRenderWindowPart->SetSelectedPosition(positionInWorldCoordinates); } } } void QmitkImageNavigatorView::OnRefetch() { if (m_IRenderWindowPart) { mitk::BaseGeometry::ConstPointer geometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldGeometry3D(); mitk::TimeGeometry::ConstPointer timeGeometry = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNull() && timeGeometry.IsNotNull()) { mitk::TimeStepType timeStep = m_IRenderWindowPart->GetActiveQmitkRenderWindow()->GetSliceNavigationController()->GetTime()->GetPos(); geometry = timeGeometry->GetGeometryForTimeStep(timeStep); SetVisibilityOfTimeSlider(timeGeometry->CountTimeSteps()); } if (geometry.IsNotNull()) { mitk::BoundingBox::BoundsArrayType bounds = geometry->GetBounds(); mitk::Point3D cornerPoint1InIndexCoordinates; cornerPoint1InIndexCoordinates[0] = bounds[0]; cornerPoint1InIndexCoordinates[1] = bounds[2]; cornerPoint1InIndexCoordinates[2] = bounds[4]; mitk::Point3D cornerPoint2InIndexCoordinates; cornerPoint2InIndexCoordinates[0] = bounds[1]; cornerPoint2InIndexCoordinates[1] = bounds[3]; cornerPoint2InIndexCoordinates[2] = bounds[5]; if (!geometry->GetImageGeometry()) { cornerPoint1InIndexCoordinates[0] += 0.5; cornerPoint1InIndexCoordinates[1] += 0.5; cornerPoint1InIndexCoordinates[2] += 0.5; cornerPoint2InIndexCoordinates[0] -= 0.5; cornerPoint2InIndexCoordinates[1] -= 0.5; cornerPoint2InIndexCoordinates[2] -= 0.5; } mitk::Point3D crossPositionInWorldCoordinates = m_IRenderWindowPart->GetSelectedPosition(); mitk::Point3D cornerPoint1InWorldCoordinates; mitk::Point3D cornerPoint2InWorldCoordinates; geometry->IndexToWorld(cornerPoint1InIndexCoordinates, cornerPoint1InWorldCoordinates); geometry->IndexToWorld(cornerPoint2InIndexCoordinates, cornerPoint2InWorldCoordinates); m_Controls.m_XWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_YWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(true); m_Controls.m_XWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); m_Controls.m_YWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); m_Controls.m_ZWorldCoordinateSpinBox->setMinimum(std::min(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); m_Controls.m_XWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0])); m_Controls.m_YWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1])); m_Controls.m_ZWorldCoordinateSpinBox->setMaximum(std::max(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2])); m_Controls.m_XWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[0]); m_Controls.m_YWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[1]); m_Controls.m_ZWorldCoordinateSpinBox->setValue(crossPositionInWorldCoordinates[2]); m_Controls.m_XWorldCoordinateSpinBox->blockSignals(false); m_Controls.m_YWorldCoordinateSpinBox->blockSignals(false); m_Controls.m_ZWorldCoordinateSpinBox->blockSignals(false); /// Calculating 'inverse direction' property. mitk::AffineTransform3D::MatrixType matrix = geometry->GetIndexToWorldTransform()->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); for (int worldAxis = 0; worldAxis < 3; ++worldAxis) { QmitkRenderWindow* renderWindow = worldAxis == 0 ? m_IRenderWindowPart->GetQmitkRenderWindow("sagittal") : worldAxis == 1 ? m_IRenderWindowPart->GetQmitkRenderWindow("coronal") : m_IRenderWindowPart->GetQmitkRenderWindow("axial"); if (renderWindow) { const mitk::BaseGeometry* rendererGeometry = renderWindow->GetRenderer()->GetCurrentWorldGeometry(); /// Because of some problems with the current way of event signalling, /// 'Modified' events are sent out from the stepper while the renderer /// does not have a geometry yet. Therefore, we do a nullptr check here. /// See bug T22122. This check can be resolved after T22122 got fixed. if (rendererGeometry) { int dominantAxis = itk::Function::Max3( inverseMatrix[0][worldAxis], inverseMatrix[1][worldAxis], inverseMatrix[2][worldAxis]); bool referenceGeometryAxisInverted = inverseMatrix[dominantAxis][worldAxis] < 0; bool rendererZAxisInverted = rendererGeometry->GetAxisVector(2)[worldAxis] < 0; /// `referenceGeometryAxisInverted` tells if the direction of the corresponding axis /// of the reference geometry is flipped compared to the 'world direction' or not. /// /// `rendererZAxisInverted` tells if direction of the renderer geometry z axis is /// flipped compared to the 'world direction' or not. This is the same as the indexing /// direction in the slice navigation controller and matches the 'top' property when /// initialising the renderer planes. (If 'top' was true then the direction is /// inverted.) /// /// The world direction can be +1 ('up') that means right, anterior or superior, or /// it can be -1 ('down') that means left, posterior or inferior, respectively. /// /// If these two do not match, we have to invert the index between the slice navigation /// controller and the slider navigator widget, so that the user can see and control /// the index according to the reference geometry, rather than the slice navigation /// controller. The index in the slice navigation controller depends on in which way /// the reference geometry has been resliced for the renderer, and it does not necessarily /// match neither the world direction, nor the direction of the corresponding axis of /// the reference geometry. Hence, it is a merely internal information that should not /// be exposed to the GUI. /// /// So that one can navigate in the same world direction by dragging the slider /// right, regardless of the direction of the corresponding axis of the reference /// geometry, we invert the direction of the controls if the reference geometry axis /// is inverted but the direction is not ('inversDirection' is false) or the other /// way around. bool inverseDirection = referenceGeometryAxisInverted != rendererZAxisInverted; QmitkSliderNavigatorWidget* navigatorWidget = worldAxis == 0 ? m_Controls.m_SliceNavigatorSagittal : worldAxis == 1 ? m_Controls.m_SliceNavigatorFrontal : m_Controls.m_SliceNavigatorAxial; navigatorWidget->SetInverseDirection(inverseDirection); // This should be a preference (see T22254) // bool invertedControls = referenceGeometryAxisInverted != inverseDirection; // navigatorWidget->SetInvertedControls(invertedControls); } } } } this->SetBorderColors(); } } void QmitkImageNavigatorView::SetVisibilityOfTimeSlider(std::size_t timeSteps) { m_Controls.m_SliceNavigatorTime->setVisible(timeSteps > 1); m_Controls.m_TimeLabel->setVisible(timeSteps > 1); } diff --git a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.cpp b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.cpp index 49fdf85bc0..9f9ea6f8ac 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.cpp +++ b/Plugins/org.mitk.gui.qt.matchpoint.visualizer/src/internal/QmitkMatchPointRegistrationVisualizer.cpp @@ -1,798 +1,798 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "org_mitk_gui_qt_matchpoint_visualizer_Activator.h" // Blueberry #include #include // Mitk #include #include #include #include #include #include #include #include "mitkRegVisDirectionProperty.h" #include "mitkRegVisStyleProperty.h" #include "mitkRegVisColorStyleProperty.h" #include "mitkRegVisPropertyTags.h" #include "mitkRegVisHelper.h" #include "mitkMatchPointPropertyTags.h" #include "mitkRegistrationHelper.h" // Qmitk #include "QmitkMatchPointRegistrationVisualizer.h" // Qt #include #include const std::string QmitkMatchPointRegistrationVisualizer::VIEW_ID = "org.mitk.views.matchpoint.visualizer"; QmitkMatchPointRegistrationVisualizer::QmitkMatchPointRegistrationVisualizer() : m_Parent(nullptr), m_internalUpdateGuard(false), m_spSelectedFOVRefNode(nullptr), m_spSelectedRegNode(nullptr) { } void QmitkMatchPointRegistrationVisualizer::SetFocus() { } void QmitkMatchPointRegistrationVisualizer::CreateConnections() { connect(m_Controls->registrationNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationVisualizer::OnNodeSelectionChanged); connect(m_Controls->fovReferenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkMatchPointRegistrationVisualizer::OnNodeSelectionChanged); connect(m_Controls->m_pbStyleGrid, SIGNAL(clicked()), this, SLOT(OnStyleButtonPushed())); connect(m_Controls->m_pbStyleGlyph, SIGNAL(clicked()), this, SLOT(OnStyleButtonPushed())); connect(m_Controls->m_pbStylePoints, SIGNAL(clicked()), this, SLOT(OnStyleButtonPushed())); connect(m_Controls->m_comboDirection, SIGNAL(currentIndexChanged(int)), this, SLOT(OnDirectionChanged(int))); connect(m_Controls->m_pbUpdateViz, SIGNAL(clicked()), this, SLOT(OnUpdateBtnPushed())); connect(m_Controls->radioColorUni, SIGNAL(toggled(bool)), m_Controls->btnUniColor, SLOT(setEnabled(bool))); connect(m_Controls->radioColorVecMag, SIGNAL(toggled(bool)), m_Controls->groupColorCoding, SLOT(setEnabled(bool))); connect(m_Controls->m_pbStyleGrid, SIGNAL(toggled(bool)), m_Controls->tabGrid, SLOT(setEnabled(bool))); connect(m_Controls->cbVevMagInterlolate, SIGNAL(toggled(bool)), this, SLOT(OnColorInterpolationChecked(bool))); connect(m_Controls->m_checkUseRefSize, SIGNAL(clicked()), this, SLOT(TransferFOVRefGeometry())); connect(m_Controls->m_checkUseRefSpacing, SIGNAL(clicked()), this, SLOT(TransferFOVRefGeometry())); connect(m_Controls->m_checkUseRefOrigin, SIGNAL(clicked()), this, SLOT(TransferFOVRefGeometry())); connect(m_Controls->m_checkUseRefOrientation, SIGNAL(clicked()), this, SLOT(TransferFOVRefGeometry())); } void QmitkMatchPointRegistrationVisualizer::Error(QString msg) { mitk::StatusBar::GetInstance()->DisplayErrorText(msg.toLatin1()); MITK_ERROR << msg.toStdString().c_str(); } void QmitkMatchPointRegistrationVisualizer::CreateQtPartControl(QWidget* parent) { m_Controls = new Ui::MatchPointRegVisControls; // create GUI widgets from the Qt Designer's .ui file m_Controls->setupUi(parent); m_Parent = parent; this->m_Controls->registrationNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls->registrationNodeSelector->SetSelectionIsOptional(false); this->m_Controls->fovReferenceNodeSelector->SetDataStorage(this->GetDataStorage()); this->m_Controls->fovReferenceNodeSelector->SetSelectionIsOptional(false); m_Controls->registrationNodeSelector->SetInvalidInfo("Select registration."); m_Controls->registrationNodeSelector->SetPopUpTitel("Select registration."); m_Controls->registrationNodeSelector->SetPopUpHint("Select the registration object whose registration visualization should be edited."); m_Controls->fovReferenceNodeSelector->SetInvalidInfo("Select a FOV reference image."); m_Controls->fovReferenceNodeSelector->SetPopUpTitel("Select a FOV reference image."); m_Controls->fovReferenceNodeSelector->SetPopUpHint("Select the the image that should be used to define the field of view (FOV) for the registration visualization. The visualization will use the image geometry (size, orientation, spacing...)."); this->ConfigureNodePredicates(); this->m_Controls->btnVecMagColorSmall->setDisplayColorName(false); this->m_Controls->btnVecMagColorMedium->setDisplayColorName(false); this->m_Controls->btnVecMagColorLarge->setDisplayColorName(false); this->m_Controls->btnVecMagColorNeg->setDisplayColorName(false); this->m_Controls->btnUniColor->setDisplayColorName(false); this->m_Controls->btnStartGridColor->setDisplayColorName(false); this->CreateConnections(); this->m_Controls->radioColorUni->setChecked(false); this->m_Controls->radioColorVecMag->setChecked(true); this->CheckInputs(); this->LoadStateFromNode(); this->ConfigureVisualizationControls(); //deactivate because currently not an implemented style this->m_Controls->m_pbStylePoints->setVisible(false); } void QmitkMatchPointRegistrationVisualizer::ConfigureNodePredicates() { m_Controls->registrationNodeSelector->SetNodePredicate(mitk::MITKRegistrationHelper::RegNodePredicate()); auto geometryCheck = [](const mitk::DataNode * node) { return node->GetData() && node->GetData()->GetGeometry(); }; mitk::NodePredicateFunction::Pointer hasGeometry = mitk::NodePredicateFunction::New(geometryCheck); auto nodePredicate = mitk::NodePredicateAnd::New(mitk::MITKRegistrationHelper::ImageNodePredicate().GetPointer(), hasGeometry.GetPointer()); m_Controls->fovReferenceNodeSelector->SetNodePredicate(nodePredicate.GetPointer()); } mitk::MAPRegistrationWrapper* QmitkMatchPointRegistrationVisualizer::GetCurrentRegistration() { mitk::MAPRegistrationWrapper* result = nullptr; if (this->m_spSelectedRegNode.IsNotNull()) { result = dynamic_cast(this->m_spSelectedRegNode->GetData()); assert(result); } return result; } mitk::DataNode::Pointer QmitkMatchPointRegistrationVisualizer::GetSelectedRegNode() const { return m_Controls->registrationNodeSelector->GetSelectedNode(); } mitk::DataNode::Pointer QmitkMatchPointRegistrationVisualizer::GetRefNodeOfReg(bool target) const { mitk::DataNode::Pointer spResult = nullptr; if (this->m_spSelectedRegNode.IsNotNull() && m_spSelectedRegNode->GetData()) { std::string nodeName; mitk::BaseProperty* uidProp; if (target) { uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgTargetData); } else { uidProp = m_spSelectedRegNode->GetData()->GetProperty(mitk::Prop_RegAlgMovingData); } if (uidProp) { //search for the target node mitk::NodePredicateDataProperty::Pointer predicate = mitk::NodePredicateDataProperty::New(mitk::Prop_UID, uidProp); spResult = this->GetDataStorage()->GetNode(predicate); } } return spResult; } mitk::DataNode::Pointer QmitkMatchPointRegistrationVisualizer::GetSelectedDataNode() { return m_Controls->fovReferenceNodeSelector->GetSelectedNode(); } void QmitkMatchPointRegistrationVisualizer::CheckInputs() { this->m_spSelectedRegNode = this->GetSelectedRegNode(); this->InitRegNode(); this->m_spSelectedFOVRefNode = this->GetSelectedDataNode(); } void QmitkMatchPointRegistrationVisualizer::ConfigureVisualizationControls() { if (!m_internalUpdateGuard) { m_internalUpdateGuard = true; m_Controls->groupViz->setVisible(this->m_spSelectedRegNode.IsNotNull()); m_Controls->m_pbUpdateViz->setEnabled(this->m_spSelectedRegNode.IsNotNull()); m_Controls->m_boxSettings->setEnabled(this->m_spSelectedRegNode.IsNotNull()); m_Controls->m_boxStyles->setEnabled(this->m_spSelectedRegNode.IsNotNull()); this->ActualizeRegInfo(this->GetCurrentRegistration()); this->m_Controls->m_checkUseRefSize->setEnabled(this->m_spSelectedRegNode.IsNotNull() && this->m_spSelectedFOVRefNode.IsNotNull()); this->m_Controls->m_checkUseRefOrigin->setEnabled(this->m_spSelectedRegNode.IsNotNull() && this->m_spSelectedFOVRefNode.IsNotNull()); this->m_Controls->m_checkUseRefSpacing->setEnabled(this->m_spSelectedRegNode.IsNotNull() && this->m_spSelectedFOVRefNode.IsNotNull()); m_internalUpdateGuard = false; } } void QmitkMatchPointRegistrationVisualizer::StoreStateInNode() { if (this->m_spSelectedRegNode.IsNotNull()) { //general this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisDirection, mitk::RegVisDirectionProperty::New(this->m_Controls->m_comboDirection->currentIndex())); this->m_spSelectedRegNode->SetBoolProperty(mitk::nodeProp_RegVisGrid, this->m_Controls->m_pbStyleGrid->isChecked()); this->m_spSelectedRegNode->SetBoolProperty(mitk::nodeProp_RegVisGlyph, this->m_Controls->m_pbStyleGlyph->isChecked()); this->m_spSelectedRegNode->SetBoolProperty(mitk::nodeProp_RegVisPoints, this->m_Controls->m_pbStylePoints->isChecked()); //Visualization if (this->m_Controls->radioColorUni->isChecked()) { this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisColorStyle, mitk::RegVisColorStyleProperty::New(0)); } else { this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisColorStyle, mitk::RegVisColorStyleProperty::New(1)); } float tmpColor[3]; tmpColor[0] = this->m_Controls->btnUniColor->color().redF(); tmpColor[1] = this->m_Controls->btnUniColor->color().greenF(); tmpColor[2] = this->m_Controls->btnUniColor->color().blueF(); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColorUni, mitk::ColorProperty::New(tmpColor), nullptr, true); tmpColor[0] = this->m_Controls->btnVecMagColorNeg->color().redF(); tmpColor[1] = this->m_Controls->btnVecMagColorNeg->color().greenF(); tmpColor[2] = this->m_Controls->btnVecMagColorNeg->color().blueF(); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor1Value, mitk::ColorProperty::New(tmpColor), nullptr, true); tmpColor[0] = this->m_Controls->btnVecMagColorSmall->color().redF(); tmpColor[1] = this->m_Controls->btnVecMagColorSmall->color().greenF(); tmpColor[2] = this->m_Controls->btnVecMagColorSmall->color().blueF(); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor2Value, mitk::ColorProperty::New(tmpColor), nullptr, true); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor2Magnitude, mitk::DoubleProperty::New(this->m_Controls->sbVecMagSmall->value()), nullptr, true); tmpColor[0] = this->m_Controls->btnVecMagColorMedium->color().redF(); tmpColor[1] = this->m_Controls->btnVecMagColorMedium->color().greenF(); tmpColor[2] = this->m_Controls->btnVecMagColorMedium->color().blueF(); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor3Value, mitk::ColorProperty::New(tmpColor), nullptr, true); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor3Magnitude, mitk::DoubleProperty::New(this->m_Controls->sbVecMagMedium->value()), nullptr, true); tmpColor[0] = this->m_Controls->btnVecMagColorLarge->color().redF(); tmpColor[1] = this->m_Controls->btnVecMagColorLarge->color().greenF(); tmpColor[2] = this->m_Controls->btnVecMagColorLarge->color().blueF(); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor4Value, mitk::ColorProperty::New(tmpColor), nullptr, true); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor4Magnitude, mitk::DoubleProperty::New(this->m_Controls->sbVecMagLarge->value()), nullptr, true); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColorInterpolate, mitk::BoolProperty::New(this->m_Controls->cbVevMagInterlolate->isChecked()), nullptr, true); //Grid Settings this->m_spSelectedRegNode->SetIntProperty(mitk::nodeProp_RegVisGridFrequence, this->m_Controls->m_sbGridFrequency->value()); this->m_spSelectedRegNode->SetBoolProperty(mitk::nodeProp_RegVisGridShowStart, this->m_Controls->m_groupShowStartGrid->isChecked()); tmpColor[0] = this->m_Controls->btnStartGridColor->color().redF(); tmpColor[1] = this->m_Controls->btnStartGridColor->color().greenF(); tmpColor[2] = this->m_Controls->btnStartGridColor->color().blueF(); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGridStartColor, mitk::ColorProperty::New(tmpColor), nullptr, true); //FOV mitk::Vector3D value; value[0] = this->m_Controls->m_sbFOVSizeX->value(); value[1] = this->m_Controls->m_sbFOVSizeY->value(); value[2] = this->m_Controls->m_sbFOVSizeZ->value(); this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVSize, mitk::Vector3DProperty::New(value)); value[0] = this->m_Controls->m_sbGridSpX->value(); value[1] = this->m_Controls->m_sbGridSpY->value(); value[2] = this->m_Controls->m_sbGridSpZ->value(); this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVSpacing, mitk::Vector3DProperty::New(value)); mitk::Point3D origin; origin[0] = this->m_Controls->m_sbFOVOriginX->value(); origin[1] = this->m_Controls->m_sbFOVOriginY->value(); origin[2] = this->m_Controls->m_sbFOVOriginZ->value(); this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVOrigin, mitk::Point3dProperty::New(origin)); mitk::Vector3D orientationRow1; mitk::Vector3D orientationRow2; mitk::Vector3D orientationRow3; - orientationRow1.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(0)); - orientationRow2.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(1)); - orientationRow3.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(2)); + orientationRow1.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(0).as_ref()); + orientationRow2.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(1).as_ref()); + orientationRow3.SetVnlVector(m_FOVRefOrientation.GetVnlMatrix().get_row(2).as_ref()); this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVOrientation1, mitk::Vector3DProperty::New(orientationRow1)); this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVOrientation2, mitk::Vector3DProperty::New(orientationRow2)); this->m_spSelectedRegNode->SetProperty(mitk::nodeProp_RegVisFOVOrientation3, mitk::Vector3DProperty::New(orientationRow3)); } } void QmitkMatchPointRegistrationVisualizer::LoadStateFromNode() { if (this->m_spSelectedRegNode.IsNotNull()) { mitk::RegVisDirectionProperty* directionProp = nullptr; if (this->m_spSelectedRegNode->GetProperty(directionProp, mitk::nodeProp_RegVisDirection)) { this->m_Controls->m_comboDirection->setCurrentIndex(directionProp->GetValueAsId()); } else { this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString( mitk::nodeProp_RegVisDirection) + QStringLiteral(" has not the assumed type.")); } bool styleActive = false; if (this->m_spSelectedRegNode->GetBoolProperty(mitk::nodeProp_RegVisGrid, styleActive)) { this->m_Controls->m_pbStyleGrid->setChecked(styleActive); this->m_Controls->tabGrid->setEnabled(styleActive); } else { this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString( mitk::nodeProp_RegVisGrid) + QStringLiteral(" has not the assumed type.")); } if (this->m_spSelectedRegNode->GetBoolProperty(mitk::nodeProp_RegVisGlyph, styleActive)) { this->m_Controls->m_pbStyleGlyph->setChecked(styleActive); } else { this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString( mitk::nodeProp_RegVisGlyph) + QStringLiteral(" has not the assumed type.")); } if (this->m_spSelectedRegNode->GetBoolProperty(mitk::nodeProp_RegVisPoints, styleActive)) { this->m_Controls->m_pbStylePoints->setChecked(styleActive); } else { this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString( mitk::nodeProp_RegVisPoints) + QStringLiteral(" has not the assumed type.")); } /////////////////////////////////////////////////////// //visualization mitk::RegVisColorStyleProperty* colorStyleProp = nullptr; if (this->m_spSelectedRegNode->GetProperty(colorStyleProp, mitk::nodeProp_RegVisColorStyle)) { this->m_Controls->radioColorUni->setChecked(colorStyleProp->GetValueAsId() == 0); this->m_Controls->radioColorVecMag->setChecked(colorStyleProp->GetValueAsId() == 1); } else { this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString( mitk::nodeProp_RegVisColorStyle) + QStringLiteral(" has not the assumed type.")); } QColor tmpColor; float colorUni[3] = { 0.0, 0.0, 0.0 }; this->m_spSelectedRegNode->GetColor(colorUni, nullptr, mitk::nodeProp_RegVisColorUni); tmpColor.setRgbF(colorUni[0], colorUni[1], colorUni[2]); this->m_Controls->btnUniColor->setColor(tmpColor); float color1[3] = { 0.0, 0.0, 0.0 }; this->m_spSelectedRegNode->GetColor(color1, nullptr, mitk::nodeProp_RegVisColor1Value); tmpColor.setRgbF(color1[0], color1[1], color1[2]); this->m_Controls->btnVecMagColorNeg->setColor(tmpColor); float color2[3] = { 0.25, 0.25, 0.25 }; this->m_spSelectedRegNode->GetColor(color2, nullptr, mitk::nodeProp_RegVisColor2Value); tmpColor.setRgbF(color2[0], color2[1], color2[2]); this->m_Controls->btnVecMagColorSmall->setColor(tmpColor); float color3[3] = { 0.5, 0.5, 0.5 }; this->m_spSelectedRegNode->GetColor(color3, nullptr, mitk::nodeProp_RegVisColor3Value); tmpColor.setRgbF(color3[0], color3[1], color3[2]); this->m_Controls->btnVecMagColorMedium->setColor(tmpColor); float color4[3] = { 1.0, 1.0, 1.0 }; this->m_spSelectedRegNode->GetColor(color4, nullptr, mitk::nodeProp_RegVisColor4Value); tmpColor.setRgbF(color4[0], color4[1], color4[2]); this->m_Controls->btnVecMagColorLarge->setColor(tmpColor); double mag2 = 0; this->m_spSelectedRegNode->GetPropertyValue(mitk::nodeProp_RegVisColor2Magnitude, mag2); double mag3 = 0; this->m_spSelectedRegNode->GetPropertyValue(mitk::nodeProp_RegVisColor3Magnitude, mag3); double mag4 = 0; this->m_spSelectedRegNode->GetPropertyValue(mitk::nodeProp_RegVisColor4Magnitude, mag4); bool interpolate = true; this->m_spSelectedRegNode->GetBoolProperty(mitk::nodeProp_RegVisColorInterpolate, interpolate); this->m_Controls->sbVecMagSmall->setValue(mag2); this->m_Controls->sbVecMagMedium->setValue(mag3); this->m_Controls->sbVecMagLarge->setValue(mag4); this->m_Controls->cbVevMagInterlolate->setChecked(interpolate); /////////////////////////////////////////////////////// //Grid general bool showStart = false; if (this->m_spSelectedRegNode->GetBoolProperty(mitk::nodeProp_RegVisGridShowStart, showStart)) { this->m_Controls->m_groupShowStartGrid->setChecked(showStart); } else { this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString( mitk::nodeProp_RegVisGridShowStart) + QStringLiteral(" is not correctly defined.")); } int gridFrequ = 5; if (this->m_spSelectedRegNode->GetIntProperty(mitk::nodeProp_RegVisGridFrequence, gridFrequ)) { this->m_Controls->m_sbGridFrequency->setValue(gridFrequ); } else { this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString( mitk::nodeProp_RegVisGridFrequence) + QStringLiteral(" is not correctly defined.")); } float colorStart[3] = { 0.0, 0.0, 0.0 }; this->m_spSelectedRegNode->GetColor(colorStart, nullptr, mitk::nodeProp_RegVisGridStartColor); tmpColor.setRgbF(colorStart[0], colorStart[1], colorStart[2]); this->m_Controls->btnStartGridColor->setColor(tmpColor); /////////////////////////////////////////////////////// //FOV mitk::Vector3DProperty* valueProp = nullptr; if (this->m_spSelectedRegNode->GetProperty(valueProp, mitk::nodeProp_RegVisFOVSize)) { this->m_Controls->m_sbFOVSizeX->setValue(valueProp->GetValue()[0]); this->m_Controls->m_sbFOVSizeY->setValue(valueProp->GetValue()[1]); this->m_Controls->m_sbFOVSizeZ->setValue(valueProp->GetValue()[2]); } else { this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString( mitk::nodeProp_RegVisFOVSize) + QStringLiteral(" is not correctly defined.")); } if (this->m_spSelectedRegNode->GetProperty(valueProp, mitk::nodeProp_RegVisFOVSpacing)) { this->m_Controls->m_sbGridSpX->setValue(valueProp->GetValue()[0]); this->m_Controls->m_sbGridSpY->setValue(valueProp->GetValue()[1]); this->m_Controls->m_sbGridSpZ->setValue(valueProp->GetValue()[2]); } else { this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString( mitk::nodeProp_RegVisFOVSpacing) + QStringLiteral(" is not correctly defined.")); } mitk::Point3dProperty* originProp = nullptr; if (this->m_spSelectedRegNode->GetProperty(originProp, mitk::nodeProp_RegVisFOVOrigin)) { this->m_Controls->m_sbFOVOriginX->setValue(originProp->GetValue()[0]); this->m_Controls->m_sbFOVOriginY->setValue(originProp->GetValue()[1]); this->m_Controls->m_sbFOVOriginZ->setValue(originProp->GetValue()[2]); } else { this->Error(QStringLiteral("Cannot configure plugin controlls correctly. Node property ") + QString( mitk::nodeProp_RegVisFOVOrigin) + QStringLiteral(" is not correctly defined.")); } mitk::Vector3DProperty* orientationProp1; mitk::Vector3DProperty* orientationProp2; mitk::Vector3DProperty* orientationProp3; if (this->m_spSelectedRegNode->GetProperty(orientationProp1, mitk::nodeProp_RegVisFOVOrientation1) && this->m_spSelectedRegNode->GetProperty(orientationProp2, mitk::nodeProp_RegVisFOVOrientation2) && this->m_spSelectedRegNode->GetProperty(orientationProp3, mitk::nodeProp_RegVisFOVOrientation3)) { this->m_Controls->m_sbFOVOriginX->setValue(originProp->GetValue()[0]); this->m_Controls->m_sbFOVOriginY->setValue(originProp->GetValue()[1]); this->m_Controls->m_sbFOVOriginZ->setValue(originProp->GetValue()[2]); m_FOVRefOrientation.GetVnlMatrix().set_row(0, orientationProp1->GetValue().GetVnlVector()); m_FOVRefOrientation.GetVnlMatrix().set_row(1, orientationProp2->GetValue().GetVnlVector()); m_FOVRefOrientation.GetVnlMatrix().set_row(2, orientationProp3->GetValue().GetVnlVector()); } else { m_FOVRefOrientation.SetIdentity(); this->Error(QStringLiteral("Cannot configure plugin controlls correctly. One of the node propertiesy ") + QString(mitk::nodeProp_RegVisFOVOrientation1) + QString(mitk::nodeProp_RegVisFOVOrientation2) + QString(mitk::nodeProp_RegVisFOVOrientation3) + QStringLiteral(" is not correctly defined.")); } this->UpdateOrientationMatrixWidget(); } } void QmitkMatchPointRegistrationVisualizer::CheckAndSetDefaultFOVRef() { //check if node has a default reference node. mitk::DataNode::Pointer defaultRef = this->GetRefNodeOfReg( this->m_Controls->m_comboDirection->currentIndex() == 1); //direction value 1 = show inverse mapping -> we need the target image used for the registration. //if there is a default node and no m_spSelectedFOVRefNode is set -> set default node and transfer values if (defaultRef.IsNotNull() && this->m_spSelectedFOVRefNode.IsNull()) { //there is a default ref and no ref lock -> select default ref and transfer its values this->m_spSelectedFOVRefNode = defaultRef; QmitkSingleNodeSelectionWidget::NodeList selection({ defaultRef }); this->m_Controls->fovReferenceNodeSelector->SetCurrentSelection(selection); this->m_Controls->m_checkUseRefSize->setChecked(true); this->m_Controls->m_checkUseRefOrigin->setChecked(true); this->m_Controls->m_checkUseRefSpacing->setChecked(true); this->m_Controls->m_checkUseRefOrientation->setChecked(true); } if (this->m_spSelectedFOVRefNode.IsNotNull()) { //auto transfere values this->TransferFOVRefGeometry(); } } void QmitkMatchPointRegistrationVisualizer::OnNodeSelectionChanged(QList /*nodes*/) { this->CheckInputs(); this->LoadStateFromNode(); this->CheckAndSetDefaultFOVRef(); this->ConfigureVisualizationControls(); } void QmitkMatchPointRegistrationVisualizer::ActualizeRegInfo(mitk::MAPRegistrationWrapper* currentReg) { std::stringstream descriptionString; m_Controls->m_teRegInfo->clear(); if (currentReg) { descriptionString << "Moving dimension: " << currentReg->GetMovingDimensions() << "
"; descriptionString << "Target dimension: " << currentReg->GetTargetDimensions() << "
"; descriptionString << "Limited moving representation: " << currentReg->HasLimitedMovingRepresentation() << "
"; descriptionString << "Limited target representation: " << currentReg->HasLimitedTargetRepresentation() << "
"; mitk::MAPRegistrationWrapper::TagMapType tagMap = currentReg->GetTags(); descriptionString << "
Tags:
"; for (mitk::MAPRegistrationWrapper::TagMapType::const_iterator pos = tagMap.begin(); pos != tagMap.end(); ++pos) { descriptionString << pos->first << " : " << pos->second << "
"; } } else { descriptionString << "no registration selected!"; } m_Controls->m_teRegInfo->insertHtml(QString::fromStdString(descriptionString.str())); } void QmitkMatchPointRegistrationVisualizer::OnDirectionChanged(int) { this->CheckAndSetDefaultFOVRef(); this->ConfigureVisualizationControls(); } void QmitkMatchPointRegistrationVisualizer::OnUpdateBtnPushed() { this->StoreStateInNode(); mitk::Geometry3D::Pointer gridDesc; unsigned int gridFrequ = 5; mitk::GetGridGeometryFromNode(this->m_spSelectedRegNode, gridDesc, gridFrequ); this->GetCurrentRegistration()->SetGeometry(gridDesc); mitk::RenderingManager::GetInstance()->ForceImmediateUpdateAll(); } void QmitkMatchPointRegistrationVisualizer::OnStyleButtonPushed() { } void QmitkMatchPointRegistrationVisualizer::OnColorInterpolationChecked(bool checked) { if (checked) { this->m_Controls->labelVecMagSmall->setText(QStringLiteral("=")); this->m_Controls->labelVecMagMedium->setText(QStringLiteral("=")); this->m_Controls->labelVecMagLarge->setText(QStringLiteral("=")); } else { this->m_Controls->labelVecMagSmall->setText(QStringLiteral(">")); this->m_Controls->labelVecMagMedium->setText(QStringLiteral(">")); this->m_Controls->labelVecMagLarge->setText(QStringLiteral(">")); } } mitk::ScalarType QmitkMatchPointRegistrationVisualizer::GetSaveSpacing(mitk::ScalarType gridRes, mitk::ScalarType spacing, unsigned int maxGridRes) const { mitk::ScalarType newSpacing = spacing; mitk::ScalarType scaling = gridRes / maxGridRes; if (scaling > 1.0) { newSpacing = spacing * scaling; } return newSpacing; } void QmitkMatchPointRegistrationVisualizer::TransferFOVRefGeometry() { if (this->m_spSelectedFOVRefNode.IsNotNull()) { assert(this->m_spSelectedFOVRefNode->GetData()); assert(this->m_spSelectedFOVRefNode->GetData()->GetGeometry()); mitk::BaseGeometry* gridRef = this->m_spSelectedFOVRefNode->GetData()->GetGeometry(); mitk::Vector3D spacing = gridRef->GetSpacing(); mitk::Point3D origin = gridRef->GetOrigin(); mitk::Geometry3D::BoundsArrayType bounds = gridRef->GetBounds(); mitk::AffineTransform3D::ConstPointer fovTransform = gridRef->GetIndexToWorldTransform(); if (this->m_Controls->m_checkUseRefSize->isChecked()) { this->m_Controls->m_sbFOVSizeX->setValue((bounds[1] - bounds[0])*spacing[0]); this->m_Controls->m_sbFOVSizeY->setValue((bounds[3] - bounds[2])*spacing[1]); this->m_Controls->m_sbFOVSizeZ->setValue((bounds[5] - bounds[4])*spacing[2]); } if (this->m_Controls->m_checkUseRefSpacing->isChecked()) { this->m_Controls->m_sbGridSpX->setValue(GetSaveSpacing((bounds[1] - bounds[0]), spacing[0], 20)); this->m_Controls->m_sbGridSpY->setValue(GetSaveSpacing((bounds[3] - bounds[2]), spacing[1], 20)); this->m_Controls->m_sbGridSpZ->setValue(GetSaveSpacing((bounds[5] - bounds[4]), spacing[2], 20)); } if (this->m_Controls->m_checkUseRefOrigin->isChecked()) { this->m_Controls->m_sbFOVOriginX->setValue(origin[0]); this->m_Controls->m_sbFOVOriginY->setValue(origin[1]); this->m_Controls->m_sbFOVOriginZ->setValue(origin[2]); } if (this->m_Controls->m_checkUseRefOrientation->isChecked()) { this->m_FOVRefOrientation = fovTransform->GetMatrix(); this->UpdateOrientationMatrixWidget(); } } } void QmitkMatchPointRegistrationVisualizer::UpdateOrientationMatrixWidget() { for (unsigned int r = 0; r < 3; ++r) { for (unsigned int c = 0; c < 3; ++c) { this->m_Controls->m_tableOrientation->item(r, c)->setText(QString::number(this->m_FOVRefOrientation.GetVnlMatrix().get(r, c))); } } } void QmitkMatchPointRegistrationVisualizer::InitRegNode() { if (this->m_spSelectedRegNode.IsNotNull()) { this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGrid, mitk::BoolProperty::New(true)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGlyph, mitk::BoolProperty::New(false)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisPoints, mitk::BoolProperty::New(false)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisDirection, mitk::RegVisDirectionProperty::New()); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColorStyle, mitk::RegVisColorStyleProperty::New(1)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColorUni, mitk::ColorProperty::New(0, 0.5, 0)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGridFrequence, mitk::IntProperty::New(3)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGridShowStart, mitk::BoolProperty::New(false)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisGridStartColor, mitk::ColorProperty::New(0.5, 0, 0)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVSize, mitk::Vector3DProperty::New(mitk::Vector3D(100.0))); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVSpacing, mitk::Vector3DProperty::New(mitk::Vector3D(5.0))); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor1Value, mitk::ColorProperty::New(0, 0, 0.5)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor2Value, mitk::ColorProperty::New(0, 0.7, 0)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor2Magnitude, mitk::DoubleProperty::New(1)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor3Value, mitk::ColorProperty::New(1, 1, 0)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor3Magnitude, mitk::DoubleProperty::New(5)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor4Value, mitk::ColorProperty::New(1, 0, 0)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColor4Magnitude, mitk::DoubleProperty::New(15)); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisColorInterpolate, mitk::BoolProperty::New(true)); mitk::Point3D origin; origin.Fill(0.0); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVOrigin, mitk::Point3dProperty::New(mitk::Point3D(origin))); mitk::Vector3D vec(0.0); vec.SetElement(0, 1.0); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVOrientation1, mitk::Vector3DProperty::New(vec)); vec.Fill(0.0); vec.SetElement(1, 1.0); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVOrientation2, mitk::Vector3DProperty::New(vec)); vec.Fill(0.0); vec.SetElement(2, 1.0); this->m_spSelectedRegNode->AddProperty(mitk::nodeProp_RegVisFOVOrientation3, mitk::Vector3DProperty::New(vec)); } } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp index 86a5468a8c..b318653e4a 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp @@ -1,382 +1,382 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "QmitkImageMaskingWidget.h" #include "mitkImage.h" #include "../../Common/QmitkDataSelectionWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { bool IsSurface(const mitk::DataNode* dataNode) { if (nullptr != dataNode) { if (nullptr != dynamic_cast(dataNode->GetData())) return true; } return false; } } static const char* const HelpText = "Select an image and a segmentation or surface"; QmitkImageMaskingWidget::QmitkImageMaskingWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent) : QmitkSegmentationUtilityWidget(timeNavigationController, parent) { m_Controls.setupUi(this); m_Controls.dataSelectionWidget->AddDataSelection(QmitkDataSelectionWidget::ImagePredicate); m_Controls.dataSelectionWidget->AddDataSelection(QmitkDataSelectionWidget::SegmentationOrSurfacePredicate); m_Controls.dataSelectionWidget->SetHelpText(HelpText); // T28795: Disable 2-d reference images since they do not work yet (segmentations are at least 3-d images with a single slice) m_Controls.dataSelectionWidget->SetPredicate(0, mitk::NodePredicateAnd::New( mitk::NodePredicateNot::New(mitk::NodePredicateDimension::New(2)), m_Controls.dataSelectionWidget->GetPredicate(0))); this->EnableButtons(false); connect(m_Controls.btnMaskImage, SIGNAL(clicked()), this, SLOT(OnMaskImagePressed())); connect(m_Controls.rbnCustom, SIGNAL(toggled(bool)), this, SLOT(OnCustomValueButtonToggled(bool))); connect(m_Controls.dataSelectionWidget, SIGNAL(SelectionChanged(unsigned int, const mitk::DataNode*)), this, SLOT(OnSelectionChanged(unsigned int, const mitk::DataNode*))); if( m_Controls.dataSelectionWidget->GetSelection(0).IsNotNull() && m_Controls.dataSelectionWidget->GetSelection(1).IsNotNull() ) { this->OnSelectionChanged(0, m_Controls.dataSelectionWidget->GetSelection(0)); } } QmitkImageMaskingWidget::~QmitkImageMaskingWidget() { } void QmitkImageMaskingWidget::OnSelectionChanged(unsigned int index, const mitk::DataNode *selection) { auto *dataSelectionWidget = m_Controls.dataSelectionWidget; auto node0 = dataSelectionWidget->GetSelection(0); if (index == 0) { dataSelectionWidget->SetPredicate(1, QmitkDataSelectionWidget::SegmentationOrSurfacePredicate); if (node0.IsNotNull()) { dataSelectionWidget->SetPredicate(1, mitk::NodePredicateAnd::New( mitk::NodePredicateGeometry::New(node0->GetData()->GetGeometry()), dataSelectionWidget->GetPredicate(1))); } } auto node1 = dataSelectionWidget->GetSelection(1); if (node0.IsNull() || node1.IsNull()) { dataSelectionWidget->SetHelpText(HelpText); this->EnableButtons(false); } else { this->SelectionControl(index, selection); } } void QmitkImageMaskingWidget::SelectionControl(unsigned int index, const mitk::DataNode* selection) { QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; mitk::DataNode::Pointer node = dataSelectionWidget->GetSelection(index); //if Image-Masking is enabled, check if image-dimension of reference and binary image is identical if( !IsSurface(dataSelectionWidget->GetSelection(1)) ) { if( dataSelectionWidget->GetSelection(0) == dataSelectionWidget->GetSelection(1) ) { dataSelectionWidget->SetHelpText("Select two different images above"); this->EnableButtons(false); return; } else if( node.IsNotNull() && selection ) { mitk::Image::Pointer referenceImage = dynamic_cast ( dataSelectionWidget->GetSelection(0)->GetData() ); mitk::Image::Pointer maskImage = dynamic_cast ( dataSelectionWidget->GetSelection(1)->GetData() ); if (maskImage.IsNull()) { dataSelectionWidget->SetHelpText("Different image sizes cannot be masked"); this->EnableButtons(false); return; } } else { dataSelectionWidget->SetHelpText(HelpText); return; } } dataSelectionWidget->SetHelpText(""); this->EnableButtons(); } void QmitkImageMaskingWidget::EnableButtons(bool enable) { m_Controls.grpBackgroundValue->setEnabled(enable); m_Controls.btnMaskImage->setEnabled(enable); } template void GetRange(const itk::Image*, double& bottom, double& top) { bottom = std::numeric_limits::lowest(); top = std::numeric_limits::max(); } void QmitkImageMaskingWidget::OnCustomValueButtonToggled(bool checked) { m_Controls.txtCustom->setEnabled(checked); } void QmitkImageMaskingWidget::OnMaskImagePressed() { //Disable Buttons during calculation and initialize Progressbar this->EnableButtons(false); mitk::ProgressBar::GetInstance()->AddStepsToDo(4); mitk::ProgressBar::GetInstance()->Progress(); QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; //create result image, get mask node and reference image mitk::Image::Pointer resultImage(nullptr); mitk::DataNode::Pointer maskingNode = dataSelectionWidget->GetSelection(1); mitk::Image::Pointer referenceImage = static_cast(dataSelectionWidget->GetSelection(0)->GetData()); if(referenceImage.IsNull() || maskingNode.IsNull() ) { MITK_ERROR << "Selection does not contain an image"; QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain an image", QMessageBox::Ok ); m_Controls.btnMaskImage->setEnabled(true); return; } //Do Image-Masking if (!IsSurface(maskingNode)) { mitk::ProgressBar::GetInstance()->Progress(); mitk::Image::Pointer maskImage = dynamic_cast ( maskingNode->GetData() ); if(maskImage.IsNull() ) { MITK_ERROR << "Selection does not contain a segmentation"; QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain a segmentation", QMessageBox::Ok ); this->EnableButtons(); return; } resultImage = this->MaskImage(referenceImage, maskImage); } //Do Surface-Masking else { mitk::ProgressBar::GetInstance()->Progress(); //1. convert surface to image mitk::Surface::Pointer surface = dynamic_cast ( maskingNode->GetData() ); //TODO Get 3D Surface of current time step if(surface.IsNull()) { MITK_ERROR << "Selection does not contain a surface"; QMessageBox::information( this, "Image and Surface Masking", "Selection does not contain a surface", QMessageBox::Ok ); this->EnableButtons(); return; } mitk::Image::Pointer maskImage = this->ConvertSurfaceToImage( referenceImage, surface ); //2. mask reference image with mask image if(maskImage.IsNotNull() && referenceImage->GetLargestPossibleRegion().GetSize() == maskImage->GetLargestPossibleRegion().GetSize() ) { resultImage = this->MaskImage( referenceImage, maskImage ); } } mitk::ProgressBar::GetInstance()->Progress(); if( resultImage.IsNull() ) { MITK_ERROR << "Masking failed"; QMessageBox::information( this, "Image and Surface Masking", "Masking failed. For more information please see logging window.", QMessageBox::Ok ); this->EnableButtons(); mitk::ProgressBar::GetInstance()->Progress(4); return; } //Add result to data storage this->AddToDataStorage( dataSelectionWidget->GetDataStorage(), resultImage, dataSelectionWidget->GetSelection(0)->GetName() + "_" + dataSelectionWidget->GetSelection(1)->GetName(), dataSelectionWidget->GetSelection(0)); this->EnableButtons(); mitk::ProgressBar::GetInstance()->Progress(); } mitk::Image::Pointer QmitkImageMaskingWidget::MaskImage(mitk::Image::Pointer referenceImage, mitk::Image::Pointer maskImage ) { mitk::ScalarType backgroundValue = 0.0; if (m_Controls.rbnMinimum->isChecked()) { backgroundValue = referenceImage->GetStatistics()->GetScalarValueMin(); } else if (m_Controls.rbnCustom->isChecked()) { auto warningTitle = QStringLiteral("Invalid custom pixel value"); bool ok = false; auto originalBackgroundValue = m_Controls.txtCustom->text().toDouble(&ok); if (!ok) { // Input is not even a number QMessageBox::warning(nullptr, warningTitle, "Please enter a valid number as custom pixel value."); return nullptr; } else { // Clamp to the numerical limits of the pixel/component type double bottom, top; if (referenceImage->GetDimension() == 4) { AccessFixedDimensionByItk_n(referenceImage, GetRange, 4, (bottom, top)); } else { AccessByItk_n(referenceImage, GetRange, (bottom, top)); } backgroundValue = std::max(bottom, std::min(originalBackgroundValue, top)); // Get rid of decimals for integral numbers auto type = referenceImage->GetPixelType().GetComponentType(); - if (type != itk::ImageIOBase::FLOAT && type != itk::ImageIOBase::DOUBLE) + if (type != itk::IOComponentEnum::FLOAT && type != itk::IOComponentEnum::DOUBLE) backgroundValue = std::round(backgroundValue); } // Ask the user for permission before correcting their input if (std::abs(originalBackgroundValue - backgroundValue) > 1e-4) { auto warningText = QString( "

The custom pixel value %1 lies not within the range of valid pixel values for the selected image.

" "

Apply the closest valid pixel value %2 instead?

").arg(originalBackgroundValue).arg(backgroundValue); auto ret = QMessageBox::warning( nullptr, warningTitle, warningText, QMessageBox::StandardButton::Apply | QMessageBox::StandardButton::Cancel, QMessageBox::StandardButton::Apply); if (QMessageBox::StandardButton::Apply != ret) return nullptr; m_Controls.txtCustom->setText(QString("%1").arg(backgroundValue)); } } auto maskFilter = mitk::MaskImageFilter::New(); maskFilter->SetInput(referenceImage); maskFilter->SetMask(maskImage); maskFilter->OverrideOutsideValueOn(); maskFilter->SetOutsideValue(backgroundValue); try { maskFilter->Update(); } catch(const itk::ExceptionObject& e) { MITK_ERROR << e.GetDescription(); return nullptr; } return maskFilter->GetOutput(); } mitk::Image::Pointer QmitkImageMaskingWidget::ConvertSurfaceToImage( mitk::Image::Pointer image, mitk::Surface::Pointer surface ) { mitk::ProgressBar::GetInstance()->AddStepsToDo(2); mitk::ProgressBar::GetInstance()->Progress(); mitk::SurfaceToImageFilter::Pointer surfaceToImageFilter = mitk::SurfaceToImageFilter::New(); surfaceToImageFilter->MakeOutputBinaryOn(); surfaceToImageFilter->SetInput(surface); surfaceToImageFilter->SetImage(image); try { surfaceToImageFilter->Update(); } catch(itk::ExceptionObject& excpt) { MITK_ERROR << excpt.GetDescription(); return nullptr; } mitk::ProgressBar::GetInstance()->Progress(); mitk::Image::Pointer resultImage = mitk::Image::New(); resultImage = surfaceToImageFilter->GetOutput(); return resultImage; } void QmitkImageMaskingWidget::AddToDataStorage(mitk::DataStorage::Pointer dataStorage, mitk::Image::Pointer segmentation, const std::string& name, mitk::DataNode::Pointer parent ) { auto dataNode = mitk::DataNode::New(); dataNode->SetName(name); dataNode->SetData(segmentation); if (parent.IsNotNull()) { mitk::LevelWindow levelWindow; parent->GetLevelWindow(levelWindow); dataNode->SetLevelWindow(levelWindow); } dataStorage->Add(dataNode, parent); }