diff --git a/.gitignore b/.gitignore index af3b8e2508..bb436560f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,72 +1,76 @@ CMakeLists.txt.user* .clang_complete .autosave ########## Git related # Patches and similar *.patch *.diff *.rej *.orig !Utilities/qtsingleapplication/*.patch !CMakeExternals/*.patch ########## IDE specific ## Office ~$* ## vim Session.vim *.swp *.swo .vimrc ## Emacs \#*\# /.emacs.desktop /.emacs.desktop.lock .elc auto-save-list tramp .\#* ## Eclipse .cproject .project .settings/ # Org-mode .org-id-locations *_archive +# Visual Studio / VS Code +.vs/ +CMakeSettings.json + ########## OS specific ## Windows files to ignore # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ ## OSX specific .DS_Store .AppleDouble .LSOverride Icon # Thumbnails ._* # Files that might appear on external disk .Spotlight-V100 .Trashes ## Linux *~ diff --git a/Applications/Diffusion/CPackConfig.cmake b/Applications/Diffusion/CPackConfig.cmake new file mode 100644 index 0000000000..a5ccbb3139 --- /dev/null +++ b/Applications/Diffusion/CPackConfig.cmake @@ -0,0 +1,28 @@ +if(CPACK_GENERATOR MATCHES "NSIS") + + # set the package header icon for MUI + SET(CPACK_PACKAGE_ICON "/home/neher/coding/mitk/mitk\\mitk.bmp") + # set the install/unistall icon used for the installer itself + # There is a bug in NSIS that does not handle full unix paths properly. + SET(CPACK_NSIS_MUI_ICON "/home/neher/coding/mitk/mitk\\mitk.ico") + SET(CPACK_NSIS_MUI_UNIICON "/home/neher/coding/mitk/mitk\\mitk.ico") + + set(CPACK_NSIS_DISPLAY_NAME "MITK Diffusion") + + # tell cpack to create links to the doc files + SET(CPACK_NSIS_MENU_LINKS + "http://www.mitk.org" "MITK Web Site" + ) + + # tell cpack the executables you want in the start menu as links + SET(CPACK_PACKAGE_EXECUTABLES "MitkDiffusion;Start MITK Diffusion" CACHE INTERNAL "Collecting windows shortcuts to executables") + + # tell cpack to create a desktop link to mitkDiffusion + SET(CPACK_CREATE_DESKTOP_LINKS "mitkDiffusion") + SET(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\mitk.ico") + SET(CPACK_NSIS_HELP_LINK "http:\\\\www.mitk.org") + SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\www.mitk.org") + SET(CPACK_NSIS_CONTACT mitk@mitk.org) + SET(CPACK_NSIS_MODIFY_PATH ON) + +endif(CPACK_GENERATOR MATCHES "NSIS") diff --git a/CMake/BuildConfigurations/DiffusionRelease.cmake b/CMake/BuildConfigurations/DiffusionRelease.cmake index 7de513dd9a..c98f87ba1f 100644 --- a/CMake/BuildConfigurations/DiffusionRelease.cmake +++ b/CMake/BuildConfigurations/DiffusionRelease.cmake @@ -1,46 +1,48 @@ message(STATUS "Configuring MITK Diffusion Release Build") # Enable non-optional external dependencies set(MITK_USE_Vigra ON CACHE BOOL "MITK Use Vigra Library" FORCE) set(MITK_USE_HDF5 ON CACHE BOOL "MITK Use HDF5 Library" FORCE) set(MITK_USE_MatchPoint ON CACHE BOOL "" FORCE) set(MITK_USE_DCMTK ON CACHE BOOL "" FORCE) set(MITK_USE_DCMQI ON CACHE BOOL "" FORCE) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) if(UNIX AND NOT APPLE) set(MITK_USE_Python ON CACHE BOOL "" FORCE) + set(MITK_USE_BetData ON CACHE BOOL "" FORCE) + set(BUILD_DiffusionPythonCmdApps ON CACHE BOOL "Build commandline tools for diffusion with python" FORCE) else() set(MITK_USE_Python OFF CACHE BOOL "" FORCE) + set(MITK_USE_BetData OFF CACHE BOOL "" FORCE) + set(BUILD_DiffusionPythonCmdApps OFF CACHE BOOL "Build commandline tools for diffusion with python" FORCE) endif() -set(MITK_USE_BetData ON CACHE BOOL "" FORCE) # Disable all apps but MITK Diffusion set(MITK_BUILD_ALL_APPS OFF CACHE BOOL "Build all MITK applications" FORCE) set(MITK_BUILD_APP_CoreApp OFF CACHE BOOL "Build the MITK CoreApp" FORCE) set(MITK_BUILD_APP_Workbench OFF CACHE BOOL "Build the MITK Workbench" FORCE) set(MITK_BUILD_APP_Diffusion ON CACHE BOOL "Build MITK Diffusion" FORCE) # Activate Diffusion Mini Apps set(BUILD_DiffusionFiberProcessingCmdApps ON CACHE BOOL "Build commandline tools for diffusion fiber processing" FORCE) set(BUILD_DiffusionFiberfoxCmdApps ON CACHE BOOL "Build commandline tools for diffusion data simulation (Fiberfox)" FORCE) set(BUILD_DiffusionMiscCmdApps ON CACHE BOOL "Build miscellaneous commandline tools for diffusion" FORCE) set(BUILD_DiffusionQuantificationCmdApps ON CACHE BOOL "Build commandline tools for diffusion quantification (IVIM, ADC, ...)" FORCE) set(BUILD_DiffusionTractographyCmdApps ON CACHE BOOL "Build commandline tools for diffusion fiber tractography" FORCE) set(BUILD_DiffusionTractographyEvaluationCmdApps ON CACHE BOOL "Build commandline tools for diffusion fiber tractography evaluation" FORCE) set(BUILD_DiffusionConnectomicsCmdApps OFF CACHE BOOL "Build commandline tools for diffusion connectomics" FORCE) -set(BUILD_DiffusionPythonCmdApps ON CACHE BOOL "Build commandline tools for diffusion with python" FORCE) # Build neither all plugins nor examples set(MITK_BUILD_ALL_PLUGINS OFF CACHE BOOL "Build all MITK plugins" FORCE) set(MITK_BUILD_EXAMPLES OFF CACHE BOOL "Build the MITK examples" FORCE) set(BUILD_TESTING OFF CACHE BOOL "Build the MITK tests" FORCE) # Activate in-application help generation set(MITK_DOXYGEN_GENERATE_QCH_FILES ON CACHE BOOL "Use doxygen to generate Qt compressed help files for MITK docs" FORCE) set(BLUEBERRY_USE_QT_HELP ON CACHE BOOL "Enable support for integrating bundle documentation into Qt Help" FORCE) # Enable console window set(MITK_SHOW_CONSOLE_WINDOW ON CACHE BOOL "Use this to enable or disable the console window when starting MITK GUI Applications" FORCE) set(MITK_VTK_DEBUG_LEAKS OFF CACHE BOOL "" FORCE) set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE) diff --git a/CMake/BuildConfigurations/DiffusionRelease.cmake b/CMake/BuildConfigurations/DiffusionRelease_NoPython.cmake similarity index 89% copy from CMake/BuildConfigurations/DiffusionRelease.cmake copy to CMake/BuildConfigurations/DiffusionRelease_NoPython.cmake index 7de513dd9a..0fb51dbc67 100644 --- a/CMake/BuildConfigurations/DiffusionRelease.cmake +++ b/CMake/BuildConfigurations/DiffusionRelease_NoPython.cmake @@ -1,46 +1,41 @@ message(STATUS "Configuring MITK Diffusion Release Build") # Enable non-optional external dependencies set(MITK_USE_Vigra ON CACHE BOOL "MITK Use Vigra Library" FORCE) set(MITK_USE_HDF5 ON CACHE BOOL "MITK Use HDF5 Library" FORCE) set(MITK_USE_MatchPoint ON CACHE BOOL "" FORCE) set(MITK_USE_DCMTK ON CACHE BOOL "" FORCE) set(MITK_USE_DCMQI ON CACHE BOOL "" FORCE) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) -if(UNIX AND NOT APPLE) - set(MITK_USE_Python ON CACHE BOOL "" FORCE) -else() - set(MITK_USE_Python OFF CACHE BOOL "" FORCE) -endif() -set(MITK_USE_BetData ON CACHE BOOL "" FORCE) +set(MITK_USE_Python OFF CACHE BOOL "" FORCE) # Disable all apps but MITK Diffusion set(MITK_BUILD_ALL_APPS OFF CACHE BOOL "Build all MITK applications" FORCE) set(MITK_BUILD_APP_CoreApp OFF CACHE BOOL "Build the MITK CoreApp" FORCE) set(MITK_BUILD_APP_Workbench OFF CACHE BOOL "Build the MITK Workbench" FORCE) set(MITK_BUILD_APP_Diffusion ON CACHE BOOL "Build MITK Diffusion" FORCE) # Activate Diffusion Mini Apps set(BUILD_DiffusionFiberProcessingCmdApps ON CACHE BOOL "Build commandline tools for diffusion fiber processing" FORCE) set(BUILD_DiffusionFiberfoxCmdApps ON CACHE BOOL "Build commandline tools for diffusion data simulation (Fiberfox)" FORCE) set(BUILD_DiffusionMiscCmdApps ON CACHE BOOL "Build miscellaneous commandline tools for diffusion" FORCE) set(BUILD_DiffusionQuantificationCmdApps ON CACHE BOOL "Build commandline tools for diffusion quantification (IVIM, ADC, ...)" FORCE) set(BUILD_DiffusionTractographyCmdApps ON CACHE BOOL "Build commandline tools for diffusion fiber tractography" FORCE) set(BUILD_DiffusionTractographyEvaluationCmdApps ON CACHE BOOL "Build commandline tools for diffusion fiber tractography evaluation" FORCE) set(BUILD_DiffusionConnectomicsCmdApps OFF CACHE BOOL "Build commandline tools for diffusion connectomics" FORCE) -set(BUILD_DiffusionPythonCmdApps ON CACHE BOOL "Build commandline tools for diffusion with python" FORCE) +set(BUILD_DiffusionPythonCmdApps OFF CACHE BOOL "Build commandline tools for diffusion with python" FORCE) # Build neither all plugins nor examples set(MITK_BUILD_ALL_PLUGINS OFF CACHE BOOL "Build all MITK plugins" FORCE) set(MITK_BUILD_EXAMPLES OFF CACHE BOOL "Build the MITK examples" FORCE) set(BUILD_TESTING OFF CACHE BOOL "Build the MITK tests" FORCE) # Activate in-application help generation set(MITK_DOXYGEN_GENERATE_QCH_FILES ON CACHE BOOL "Use doxygen to generate Qt compressed help files for MITK docs" FORCE) set(BLUEBERRY_USE_QT_HELP ON CACHE BOOL "Enable support for integrating bundle documentation into Qt Help" FORCE) # Enable console window set(MITK_SHOW_CONSOLE_WINDOW ON CACHE BOOL "Use this to enable or disable the console window when starting MITK GUI Applications" FORCE) set(MITK_VTK_DEBUG_LEAKS OFF CACHE BOOL "" FORCE) set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE) diff --git a/CMake/PackageDepends/MITK_OpenGL_Config.cmake b/CMake/PackageDepends/MITK_OpenGL_Config.cmake index 14360f47f9..a322942bd5 100644 --- a/CMake/PackageDepends/MITK_OpenGL_Config.cmake +++ b/CMake/PackageDepends/MITK_OpenGL_Config.cmake @@ -1,4 +1,5 @@ +set(OpenGL_GL_PREFERENCE LEGACY) find_package(OpenGL REQUIRED) list(APPEND ALL_INCLUDE_DIRECTORIES ${OPENGL_INCLUDE_DIR}) list(APPEND ALL_LIBRARIES ${OPENGL_LIBRARIES}) diff --git a/CMake/mitkFunctionGetMSVCVersion.cmake b/CMake/mitkFunctionGetMSVCVersion.cmake index 551c3e5d55..e12baec1a3 100644 --- a/CMake/mitkFunctionGetMSVCVersion.cmake +++ b/CMake/mitkFunctionGetMSVCVersion.cmake @@ -1,26 +1,25 @@ -#! \brief Get diverse visual studio ids not directly provided by CMake -#! -#! Sets the following variables in the parent scope -#! VISUAL_STUDIO_VERSION_MAJOR - The Visual Studio Version -#! VISUAL_STUDIO_PRODUCT_NAME - The Visual Studio Product Name - function(mitkFunctionGetMSVCVersion ) if(MSVC) - if(MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 2000) + if(MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) set(VISUAL_STUDIO_PRODUCT_NAME "Visual Studio 2017" PARENT_SCOPE) set(VISUAL_STUDIO_VERSION_MAJOR 14 PARENT_SCOPE) - string(SUBSTRING ${MSVC_VERSION} 3 -1 version_minor) + string(SUBSTRING ${MSVC_VERSION} 2 -1 version_minor) + set(VISUAL_STUDIO_VERSION_MINOR ${version_minor} PARENT_SCOPE) + elseif(MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1930) + set(VISUAL_STUDIO_PRODUCT_NAME "Visual Studio 2019" PARENT_SCOPE) + set(VISUAL_STUDIO_VERSION_MAJOR 14 PARENT_SCOPE) + string(SUBSTRING ${MSVC_VERSION} 2 -1 version_minor) set(VISUAL_STUDIO_VERSION_MINOR ${version_minor} PARENT_SCOPE) else() message(WARNING "Unknown Visual Studio version ${MSVC_VERSION} (CMake/mitkFunctionGetMSVCVersion.cmake)") endif() - if("${CMAKE_GENERATOR}" MATCHES ".*Win64") + if(CMAKE_VS_PLATFORM_NAME STREQUAL x64) set(CMAKE_LIBRARY_ARCHITECTURE x64 PARENT_SCOPE) else() set(CMAKE_LIBRARY_ARCHITECTURE x86 PARENT_SCOPE) endif() endif() endfunction() diff --git a/CMakeExternals/Boost.cmake b/CMakeExternals/Boost.cmake index 6be4211895..0fd9f0ecd3 100644 --- a/CMakeExternals/Boost.cmake +++ b/CMakeExternals/Boost.cmake @@ -1,332 +1,332 @@ #----------------------------------------------------------------------------- # Boost #----------------------------------------------------------------------------- include(mitkFunctionGetMSVCVersion) #[[ Sanity checks ]] if(DEFINED BOOST_ROOT AND NOT EXISTS ${BOOST_ROOT}) message(FATAL_ERROR "BOOST_ROOT variable is defined but corresponds to non-existing directory") endif() string(REPLACE "^^" ";" MITK_USE_Boost_LIBRARIES "${MITK_USE_Boost_LIBRARIES}") set(proj Boost) set(proj_DEPENDENCIES ) set(Boost_DEPENDS ${proj}) if(NOT DEFINED BOOST_ROOT AND NOT MITK_USE_SYSTEM_Boost) #[[ Reset variables. ]] set(patch_cmd "") set(configure_cmd "") set(install_cmd "") set(BOOST_ROOT ${ep_prefix}) if(WIN32) set(BOOST_LIBRARYDIR "${BOOST_ROOT}/lib") endif() #[[ If you update Boost, make sure that the FindBoost module of the minimum required version of CMake supports the new version of Boost. In case you are using a higher version of CMake, download at least the source code of the minimum required version of CMake to look into the right version of the FindBoost module: /share/cmake-/Modules/FindBoost.cmake Search for a list called _Boost_KNOWN_VERSIONS. If the new version is not included in this list, you have three options: * Update the minimum required version of CMake. This may require adaptions of other parts of our CMake scripts and has the most impact on other MITK developers. Yet this is the safest and cleanest option. * Set Boost_ADDITIONAL_VERSIONS (see the documentation of the FindBoost module). As Boost libraries and dependencies between them are hard-coded in the FindBoost module only for known versions, this may cause trouble for other MITK developers relying on new components of Boost or components with changed dependencies. * Copy a newer version of the FindBoost module into our CMake directory. Our CMake directory has a higher precedence than the default CMake module directory. Doublecheck if the minimum required version of CMake is able to process the newer version of the FindBoost module. Also, DO NOT FORGET to mention this option right above the call of cmake_minimum_required() in the top-level CMakeLists.txt file AND in this file right above the set(url) command below so if we update the minimum required version of CMake or use another option in the future, we do not forget to remove our copy of the FindBoost module again. ]] set(url "${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/boost_1_68_0.7z") set(md5 ae25f29cdb82cf07e8e26187ddf7d330) if(WIN32) #[[ See Task T25540 for details. Can be removed with Boost version greater 1.68. In case the patch fails a "cd ." is executed to force the return return value to be 0 (success). We need this because we reuse the extracted source files and patching an already patched file returns an error code that we can ignore. ]] set(patch_cmd ${PATCH_COMMAND} --binary -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/Boost.patch || cd .) endif() if(MITK_USE_Boost_LIBRARIES) #[[ Boost has a two-step build process. In the first step, a bootstrap script is called to build b2, an executable that is used to actually build Boost in the second step. The bootstrap script expects a toolset (compiler) argument that is used to build the b2 executable. The scripts and their expected argument format differ between Windows and Unix. ]] if(WIN32) mitkFunctionGetMSVCVersion() if(VISUAL_STUDIO_VERSION_MINOR EQUAL 0) #[[ Use just the major version in the toolset name. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}) elseif(VISUAL_STUDIO_VERSION_MAJOR EQUAL 14 AND VISUAL_STUDIO_VERSION_MINOR GREATER 0) #[[ There is no generic way of deducing the Boost toolset from the current minor version of Visual Studio 2017. All we can do for now is to assume that for all versions greater than 14.10 and less than 15.0 toolset vc141 is the right choice. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}1) else() #[[ Fallback to the generic case. Be prepared to add another elseif branch above for future versions of Visual Studio. ]] set(bootstrap_args vc${VISUAL_STUDIO_VERSION_MAJOR}${VISUAL_STUDIO_VERSION_MINOR}) endif() else() #[[ We support GCC and Clang on Unix. On macOS, the toolset must be set to darwin. The actual compiler for all of these toolkits is set further below, after the bootstrap script but before b2. ]] if(APPLE) set(toolset darwin) elseif(CMAKE_CXX_COMPILER_ID STREQUAL GNU) set(toolset gcc) elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang) set(toolset clang) endif() if(toolset) set(bootstrap_args --with-toolset=${toolset}) endif() #[[ At least give it a shot if the toolset is something else and let the bootstrap script decide on the toolset by not passing any argument. ]] endif() #[[ The call of b2 is more complex. b2 arguments are grouped into options and properties. Options follow the standard format for arguments while properties are plain key-value pairs. ]] set(b2_options --build-dir= --stagedir= --ignore-site-config #[[ Build independent of any site.config file ]] -q #[[ Stop at first error ]] ) if(APPLE AND CMAKE_OSX_SYSROOT) #[[ Specify the macOS platform SDK to be used. ]] list(APPEND b2_options --sysroot=${CMAKE_OSX_SYSROOT}) endif() foreach(lib ${MITK_USE_Boost_LIBRARIES}) list(APPEND b2_options --with-${lib}) endforeach() set(b2_properties threading=multi runtime-link=shared "cxxflags=${MITK_CXX14_FLAG} ${CMAKE_CXX_FLAGS}" ) if(CMAKE_SIZEOF_VOID_P EQUAL 8) list(APPEND b2_properties address-model=64) else() list(APPEND b2_properties address-model=32) endif() if(BUILD_SHARED_LIBS) list(APPEND b2_properties link=shared) else() list(APPEND b2_properties link=static) endif() list(APPEND b2_properties "\ $<$:variant=debug>\ $<$:variant=release>\ $<$:variant=release>\ $<$:variant=release>") if(WIN32) set(bootstrap_cmd if not exist b2.exe \( call bootstrap.bat ${bootstrap_args} \)) set(b2_cmd b2 ${b2_options} ${b2_properties} stage) else() - set(bootstrap_cmd test -e ./b2 || ./bootstrap.sh ${bootstrap_args}) + set(bootstrap_cmd #[[ test -e ./b2 || ]] ./bootstrap.sh ${bootstrap_args}) set(b2_cmd ./b2 ${b2_options} ${b2_properties} stage) #[[ We already told Boost if we want to use GCC or Clang but so far we were not able to specify the exact same compiler we set in CMake when configuring the MITK superbuild for the first time. For example, this can be different from the system default when multiple versions of the same compiler are installed at the same time. The bootstrap script creates a configuration file for b2 that should be modified if necessary before b2 is called. We look for a line like using gcc ; and replace it with something more specific like using gcc : : /usr/bin/gcc-7.3 ; We use the stream editor sed for the replacement but as macOS is based on BSD Unix, we use the limited but portable BSD syntax instead of the more powerful GNU syntax. We also use | instead of the more commonly used / separator for sed because the replacement contains slashes. ]] if(toolset) set(configure_cmd sed -i.backup "\ s|\ using[[:space:]][[:space:]]*${toolset}[[:space:]]*$|\ using ${toolset} : : ${CMAKE_CXX_COMPILER} $|\ g" /project-config.jam ) endif() endif() endif() if(WIN32) set(dummy_cmd cd .) else() set(dummy_cmd true) #[[ "cd ." does not work reliably ]] endif() if(NOT patch_cmd) set(patch_cmd ${dummy_cmd}) #[[ Do nothing ]] endif() if(NOT configure_cmd) set(configure_cmd ${dummy_cmd}) #[[ Do nothing ]] endif() if(WIN32) set(install_cmd if not exist $ \( ${CMAKE_COMMAND} -E copy_directory /boost /include/boost \) ) else() set(install_cmd - test -e /include/boost/config.hpp || + # test -e /include/boost/config.hpp || ${CMAKE_COMMAND} -E copy_directory /boost /include/boost ) endif() ExternalProject_Add(${proj} URL ${url} URL_MD5 ${md5} PATCH_COMMAND ${patch_cmd} CONFIGURE_COMMAND ${configure_cmd} BUILD_COMMAND "" INSTALL_COMMAND ${install_cmd} ) ExternalProject_Add_Step(${proj} bootstrap COMMAND ${bootstrap_cmd} DEPENDEES patch DEPENDERS configure WORKING_DIRECTORY ) ExternalProject_Add_Step(${proj} b2 COMMAND ${b2_cmd} DEPENDEES bootstrap DEPENDERS build WORKING_DIRECTORY ) if(WIN32) #[[ Reuse already extracted files. ]] set(stamp_dir ${ep_prefix}/src/Boost-stamp) configure_file( ${CMAKE_CURRENT_LIST_DIR}/extract-Boost.replacement.cmake ${stamp_dir}/extract-Boost.replacement.cmake COPYONLY) ExternalProject_Add_Step(${proj} pre_download COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-pre_download.cmake DEPENDEES mkdir DEPENDERS download WORKING_DIRECTORY ${stamp_dir} ) endif() if(MITK_USE_Boost_LIBRARIES) if(WIN32) #[[ Move DLLs from lib to bin directory. ]] ExternalProject_Add_Step(${proj} post_install COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-post_install-WIN32.cmake DEPENDEES install WORKING_DIRECTORY /lib ) elseif(APPLE) #[[ Boost does not follow the common practice of either using rpath or absolute paths for referencing dependencies. We have to use the install_name_tool to fix this. ]] ExternalProject_Add_Step(${proj} post_install COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/Boost-post_install-APPLE.cmake DEPENDEES install WORKING_DIRECTORY /lib ) endif() endif() else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/CMakeExternals/OpenIGTLink-54df50de.patch b/CMakeExternals/OpenIGTLink-54df50de.patch deleted file mode 100644 index 22e1964f13..0000000000 --- a/CMakeExternals/OpenIGTLink-54df50de.patch +++ /dev/null @@ -1,36 +0,0 @@ -diff -burN OpenIGTLink-master/OpenIGTLinkConfig.cmake.in OpenIGTLink/OpenIGTLinkConfig.cmake.in ---- OpenIGTLink-master/OpenIGTLinkConfig.cmake.in 2014-08-21 18:53:57.000000000 +0200 -+++ OpenIGTLink/OpenIGTLinkConfig.cmake.in 2015-02-05 01:08:29.885874473 +0100 -@@ -48,7 +48,7 @@ - # A list of all libraries for OpenIGTLink. Those listed here should - # automatically pull in their dependencies. - #SET(OpenIGTLink_LIBRARIES OpenIGTLinkAlgorithms OpenIGTLinkStatistics OpenIGTLinkFEM) --SET(OpenIGTLink_LIBRARIES OpenIGTLink) -+SET(OpenIGTLink_LIBRARIES optimized;OpenIGTLink;debug;OpenIGTLink@CMAKE_DEBUG_POSTFIX@) - - # The OpenIGTLink library dependencies. - IF(NOT OpenIGTLink_NO_LIBRARY_DEPENDS AND -diff -burN OpenIGTLink-master/Source/CMakeLists.txt OpenIGTLink/Source/CMakeLists.txt ---- OpenIGTLink-master/Source/CMakeLists.txt 2014-08-21 18:53:57.000000000 +0200 -+++ OpenIGTLink/Source/CMakeLists.txt 2015-02-05 00:34:34.086991641 +0100 -@@ -65,6 +65,7 @@ - igtlutil/igtl_image.h - igtlutil/igtl_position.h - igtlutil/igtl_transform.h -+ igtlutil/igtl_status.h - igtlutil/igtl_types.h - igtlutil/igtl_util.h - igtlutil/igtl_capability.h - -diff -burN OpenIGTLink-master/Source/igtlSocket.cxx OpenIGTLink/Source/igtlSocket.cxx ---- OpenIGTLink-master/Source/igtlSocket.cxx 2014-08-21 18:53:57.000000000 +0200 -+++ OpenIGTLink/Source/igtlSocket.cxx 2015-02-05 00:34:34.086991641 +0100 -@@ -51,7 +51,7 @@ - #define WSA_VERSION MAKEWORD(1,1) - #define igtlCloseSocketMacro(sock) (closesocket(sock)) - #else --#define igtlCloseSocketMacro(sock) (shutdown(sock, 2)) -+#define igtlCloseSocketMacro(sock) ({shutdown(sock, 2); close(sock);}) - #endif - - namespace igtl diff --git a/CMakeExternals/OpenIGTLink-92bc3d7b.patch b/CMakeExternals/OpenIGTLink-92bc3d7b.patch new file mode 100644 index 0000000000..314b33090e --- /dev/null +++ b/CMakeExternals/OpenIGTLink-92bc3d7b.patch @@ -0,0 +1,65 @@ +--- OpenIGTLink/Source/CMakeLists.txt.original 2017-06-28 12:28:12.000000000 +0200 ++++ OpenIGTLink/Source/CMakeLists.txt 2019-03-31 04:04:46.392945177 +0200 +@@ -71,12 +71,11 @@ + igtlTransformMessage.cxx + ) + +-SET(OpenIGTLink_INCLUDE_FILES) +-IF( MSVC OR ${CMAKE_GENERATOR} MATCHES "Xcode" ) +- LIST(APPEND OpenIGTLink_INCLUDE_FILES ++SET(OpenIGTLink_INCLUDE_FILES + igtlutil/igtl_header.h + igtlutil/igtl_image.h + igtlutil/igtl_position.h ++ igtlutil/igtl_status.h + igtlutil/igtl_transform.h + igtlutil/igtl_types.h + igtlutil/igtl_util.h +@@ -117,7 +116,6 @@ + igtlWindows.h + igtlCommon.h + ) +-ENDIF() + + # Add support for OpenIGTLink version 2 + IF (${OpenIGTLink_PROTOCOL_VERSION} GREATER "1" ) +@@ -150,7 +148,7 @@ + igtlBindMessage.cxx + igtlNDArrayMessage.cxx + ) +- IF( MSVC OR ${CMAKE_GENERATOR} MATCHES "Xcode" ) ++ + LIST(APPEND OpenIGTLink_INCLUDE_FILES + igtlutil/igtl_colortable.h + igtlutil/igtl_imgmeta.h +@@ -180,7 +178,6 @@ + igtlBindMessage.h + igtlNDArrayMessage.h + ) +- ENDIF() + ENDIF() + + # Add support for OpenIGTLink version 3 +@@ -191,14 +188,13 @@ + igtlutil/igtl_command.c + igtlutil/igtl_query.c + ) +- IF( MSVC OR ${CMAKE_GENERATOR} MATCHES "Xcode" ) ++ + LIST(APPEND OpenIGTLink_INCLUDE_FILES + igtlCommandMessage.h + igtlQueryMessage.h + igtlutil/igtl_command.h + igtlutil/igtl_query.h + ) +- ENDIF() + ENDIF() + + ADD_LIBRARY(OpenIGTLink ${OpenIGTLink_SOURCES} ${OpenIGTLink_INCLUDE_FILES}) +@@ -227,4 +223,4 @@ + INSTALL(TARGETS OpenIGTLink EXPORT OpenIGTLink + RUNTIME DESTINATION ${OpenIGTLink_INSTALL_BIN_DIR} COMPONENT RuntimeLibraries + LIBRARY DESTINATION ${OpenIGTLink_INSTALL_LIB_DIR} COMPONENT RuntimeLibraries +- ARCHIVE DESTINATION ${OpenIGTLink_INSTALL_LIB_DIR} COMPONENT Development) +\ No newline at end of file ++ ARCHIVE DESTINATION ${OpenIGTLink_INSTALL_LIB_DIR} COMPONENT Development) diff --git a/CMakeExternals/OpenIGTLink.cmake b/CMakeExternals/OpenIGTLink.cmake index 691f4c706c..c879f124d3 100644 --- a/CMakeExternals/OpenIGTLink.cmake +++ b/CMakeExternals/OpenIGTLink.cmake @@ -1,53 +1,53 @@ #----------------------------------------------------------------------------- # OpenIGTLink #----------------------------------------------------------------------------- if(MITK_USE_OpenIGTLink) # Sanity checks if(DEFINED OpenIGTLink_DIR AND NOT EXISTS ${OpenIGTLink_DIR}) message(FATAL_ERROR "OpenIGTLink_DIR variable is defined but corresponds to non-existing directory") endif() set(proj OpenIGTLink) set(proj_DEPENDENCIES ) set(${proj}_DEPENDS ${proj}) if(NOT DEFINED OpenIGTLink_DIR) set(additional_cmake_args ) if(CTEST_USE_LAUNCHERS) set(additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() ExternalProject_Add(${proj} - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/OpenIGTLink-54df50de.tar.gz - URL_MD5 b9fd8351b059f4ec615f2dfd74ab2458 - PATCH_COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/OpenIGTLink-54df50de.patch + URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/OpenIGTLink-release-3.0.tar.gz + URL_MD5 0a759655da037f6df2087dd2690d1ae2 + PATCH_COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/OpenIGTLink-92bc3d7b.patch CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} -DBUILD_EXAMPLES:BOOL=OFF -DOpenIGTLink_PROTOCOL_VERSION_2:BOOL=ON -DOpenIGTLink_INSTALL_LIB_DIR:STRING=lib -DOpenIGTLink_INSTALL_PACKAGE_DIR:STRING=lib/cmake/OpenIGTLink -DOpenIGTLink_INSTALL_NO_DOCUMENTATION:BOOL=ON CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) set(OpenIGTLink_DIR "${ep_prefix}/lib/cmake/OpenIGTLink") else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 8d0362a489..7888dc5079 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1425 +1,1444 @@ set(MITK_CMAKE_MINIMUM_REQUIRED_VERSION 3.13) cmake_minimum_required(VERSION ${MITK_CMAKE_MINIMUM_REQUIRED_VERSION}) #----------------------------------------------------------------------------- # See https://cmake.org/cmake/help/v3.10/manual/cmake-policies.7.html for details #----------------------------------------------------------------------------- set(project_policies ) foreach(policy ${project_policies}) if(POLICY ${policy}) cmake_policy(SET ${policy} NEW) endif() endforeach() #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON) if(MITK_USE_SUPERBUILD) project(MITK-superbuild) set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR}) else() project(MITK VERSION 2018.04.99) include_directories(SYSTEM ${MITK_SUPERBUILD_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # MITK Extension Feature #----------------------------------------------------------------------------- set(MITK_EXTENSION_DIRS "" CACHE STRING "") set(MITK_DIR_PLUS_EXTENSION_DIRS ${MITK_SOURCE_DIR} ${MITK_EXTENSION_DIRS}) #----------------------------------------------------------------------------- # Update CMake module path #----------------------------------------------------------------------------- set(MITK_CMAKE_DIR ${MITK_SOURCE_DIR}/CMake) set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_CMAKE_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMake) get_filename_component(MITK_CMAKE_EXTENSION_DIR ${MITK_CMAKE_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_CMAKE_EXTENSION_DIR}) list(APPEND CMAKE_MODULE_PATH ${MITK_CMAKE_EXTENSION_DIR}) endif() endforeach() #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- # Standard CMake macros include(FeatureSummary) include(CTestUseLaunchers) include(CMakeParseArguments) include(FindPackageHandleStandardArgs) # MITK macros include(mitkFunctionGetGccVersion) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkMacroEmptyExternalProject) include(mitkFunctionGenerateProjectXml) include(mitkFunctionEnableBuildConfiguration) include(mitkFunctionWhitelists) include(mitkFunctionAddExternalProject) include(mitkFunctionAddLibrarySearchPaths) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #----------------------------------------------------------------------------- # Check miminum macOS version #----------------------------------------------------------------------------- # The minimum supported macOS version is 10.13. If you use a version less than 10.13, there is no guarantee that the build still works. if(APPLE) exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE macos_version) if (macos_version VERSION_LESS "10.13") message(WARNING "Detected macOS version \"${macos_version}\" is not supported anymore. Minimum required macOS version is at least 10.13.") endif() if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.13) message(WARNING "Detected macOS deployment target \"${CMAKE_OSX_DEPLOYMENT_TARGET}\" is not supported anymore. Minimum required macOS version is at least 10.13.") endif() endif() #----------------------------------------------------------------------------- # Check miminum compiler versions #----------------------------------------------------------------------------- if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # require at least gcc 4.9 as provided by ppa:ubuntu-toolchain-r/test for Ubuntu 14.04 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) message(FATAL_ERROR "GCC version must be at least 4.9 If you are using Ubuntu 14.04, you can easily install gcc and g++ 4.9 (or any later version available) in addition to your version ${CMAKE_CXX_COMPILER_VERSION}: sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install gcc-4.9 g++-4.9 Make sure to explicitly specify these compilers when configuring MITK: CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc-4.9 CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++-4.9 For more information on the proposed PPA see the Toolchain Updates section of https://wiki.ubuntu.com/ToolChain.") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # require at least clang 3.4 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4) message(FATAL_ERROR "Clang version must be at least 3.4") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") # require at least clang 5.0 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) message(FATAL_ERROR "Apple Clang version must be at least 5.0") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - # require at least Visual Studio 2015 + # 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) #----------------------------------------------------------------------------- macro(env_option name doc value) set(_value $ENV{${name}}) if("${_value}" STREQUAL "") set(_value ${value}) endif() option(${name} "${doc}" ${_value}) endmacro() # ----------------------------------------- # 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) env_option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) env_option(MITK_BUILD_EXAMPLES "Build the MITK Examples" OFF) option(MITK_ENABLE_PIC_READER "Enable support for reading the DKFZ pic file format." ON) mark_as_advanced(MITK_BUILD_ALL_APPS MITK_ENABLE_PIC_READER ) # ----------------------------------------- # Qt version related variables env_option(MITK_USE_Qt5 "Use Qt 5 library" ON) if(MITK_USE_Qt5) set(MITK_QT5_MINIMUM_VERSION 5.11.1) 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(_compiler "msvc${CMAKE_MATCH_1}") + set(_compilers "msvc${CMAKE_MATCH_1}") + elseif(CMAKE_GENERATOR MATCHES "Ninja") + include(mitkFunctionGetMSVCVersion) + mitkFunctionGetMSVCVersion() + if(VISUAL_STUDIO_PRODUCT_NAME MATCHES "^Visual Studio ([0-9]+)") + set(_compilers "msvc${CMAKE_MATCH_1}") + endif() + endif() + + if(_compilers MATCHES "[0-9]+") + if (CMAKE_MATCH_0 EQUAL 2019) + list(APPEND _compilers "msvc2017") # Binary compatible to 2019 + endif() endif() else() set(_dir_candidates ~/Qt) if(APPLE) - set(_compiler clang) + set(_compilers clang) else() list(APPEND _dir_candidates /opt/Qt) - set(_compiler gcc) + set(_compilers gcc) endif() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(_compiler "${_compiler}_64") + 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) - set(_glob_expression "${_dir_candidate}/5.*/${_compiler}") - file(GLOB _hints ${_glob_expression}) - list(SORT _hints) - list(APPEND MITK_QT5_HINTS ${_hints}) + 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() set_property(GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS "") include(CMakeExternals/ExternalProjectList.cmake) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMakeExternals) get_filename_component(MITK_CMAKE_EXTERNALS_EXTENSION_DIR ${MITK_CMAKE_EXTERNALS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake) include(${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake) endif() endforeach() # ----------------------------------------- # Other MITK_USE_* options not related to # external projects build via the # MITK superbuild env_option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) env_option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF) #----------------------------------------------------------------------------- # Build configurations #----------------------------------------------------------------------------- set(_buildConfigs "Custom") file(GLOB _buildConfigFiles CMake/BuildConfigurations/*.cmake) foreach(_buildConfigFile ${_buildConfigFiles}) get_filename_component(_buildConfigFile ${_buildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_buildConfigFile}) endforeach() foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) file(GLOB _extBuildConfigFiles ${MITK_EXTENSION_DIR}/CMake/BuildConfigurations/*.cmake) foreach(_extBuildConfigFile ${_extBuildConfigFiles}) get_filename_component(_extBuildConfigFile ${_extBuildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_extBuildConfigFile}) endforeach() list(REMOVE_DUPLICATES _buildConfigs) endforeach() set(MITK_BUILD_CONFIGURATION "Custom" CACHE STRING "Use pre-defined MITK configurations") set_property(CACHE MITK_BUILD_CONFIGURATION PROPERTY STRINGS ${_buildConfigs}) mitkFunctionEnableBuildConfiguration() mitkFunctionCreateWhitelistPaths(MITK) mitkFunctionFindWhitelists(MITK) # ----------------------------------------- # Custom dependency logic option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") if(MITK_USE_cpprestsdk) find_package(OpenSSL QUIET) if(NOT OpenSSL_FOUND) set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK).\n") if(UNIX) if(APPLE) set(openssl_message "${openssl_message}Please install it using your favorite package management " "system (i.e. Homebrew or MacPorts).\n") else() set(openssl_message "${openssl_message}Please install the dev package of OpenSSL (i.e. libssl-dev).\n") endif() else() set(openssl_message "${openssl_message}Please install Win32 OpenSSL:\n" " https://slproweb.com/products/Win32OpenSSL.html\n") endif() set(openssl_message "${openssl_message}If it still cannot be found, you can hint CMake to find OpenSSL by " "adding/setting the OPENSSL_ROOT_DIR variable to the root directory of an " "OpenSSL installation. Make sure to clear variables of partly found " "versions of OpenSSL before, or they will be mixed up.") message(FATAL_ERROR ${openssl_message}) endif() list(APPEND MITK_USE_Boost_LIBRARIES date_time regex system) if(UNIX) list(APPEND MITK_USE_Boost_LIBRARIES atomic chrono filesystem random thread) endif() list(REMOVE_DUPLICATES MITK_USE_Boost_LIBRARIES) set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "A semi-colon separated list of required Boost libraries" FORCE) endif() if(MITK_USE_Python) set(MITK_USE_ZLIB ON) find_package(PythonLibs 3 REQUIRED) find_package(PythonInterp 3 REQUIRED) if ( NOT PYTHON_VERSION_STRING VERSION_EQUAL PYTHONLIBS_VERSION_STRING) message(SEND_ERROR "Inconsistent python versions found: interpreter ${PYTHON_VERSION_STRING} vs. libs ${PYTHONLIBS_VERSION_STRING}" ) endif() find_package(Numpy REQUIRED) endif() if(BUILD_TESTING AND NOT MITK_USE_CppUnit) message("> Forcing MITK_USE_CppUnit to ON because BUILD_TESTING=ON") set(MITK_USE_CppUnit ON CACHE BOOL "Use CppUnit for unit tests" FORCE) endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("> Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() #----------------------------------------------------------------------------- # Pixel type multiplexing #----------------------------------------------------------------------------- # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES) set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) string(REPLACE "," ";" _integral_types ${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES}) string(REPLACE "," ";" _floating_types ${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES}) foreach(_scalar_type ${_integral_types} ${_floating_types}) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}itk::VariableLengthVector<${_scalar_type}>,") endforeach() string(LENGTH "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" _length) math(EXPR _length "${_length} - 1") string(SUBSTRING "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" 0 ${_length} MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES ${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES} CACHE STRING "List of vector pixel types used in AccessByItk and InstantiateAccessFunction macros for itk::VectorImage types" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() #----------------------------------------------------------------------------- # Project.xml #----------------------------------------------------------------------------- # A list of topologically ordered targets set(CTEST_PROJECT_SUBPROJECTS) list(APPEND CTEST_PROJECT_SUBPROJECTS MITK-Core MITK-CoreUI MITK-IGT MITK-ToF MITK-DTI MITK-Modules # all modules not contained in a specific subproject MITK-Plugins # all plugins not contained in a specific subproject MITK-Examples Unlabeled # special "subproject" catching all unlabeled targets and tests ) # Configure CTestConfigSubProject.cmake that could be used by CTest scripts configure_file(${MITK_SOURCE_DIR}/CTestConfigSubProject.cmake.in ${MITK_BINARY_DIR}/CTestConfigSubProject.cmake) if(CTEST_PROJECT_ADDITIONAL_TARGETS) # those targets will be executed at the end of the ctest driver script # and they also get their own subproject label set(subproject_list "${CTEST_PROJECT_SUBPROJECTS};${CTEST_PROJECT_ADDITIONAL_TARGETS}") else() set(subproject_list "${CTEST_PROJECT_SUBPROJECTS}") endif() # Generate Project.xml file expected by the CTest driver script mitkFunctionGenerateProjectXml(${MITK_BINARY_DIR} MITK "${subproject_list}" ${MITK_USE_SUPERBUILD}) #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") # Print configuration summary message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL) return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(WriteBasicConfigVersionFile) include(CheckCXXSourceCompiles) include(GenerateExportHeader) include(mitkFunctionAddCustomModuleTest) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionCompileSnippets) include(mitkFunctionConfigureVisualStudioUserProjectFile) include(mitkFunctionConvertXPSchema) include(mitkFunctionCreateBlueBerryApplication) include(mitkFunctionCreateCommandLineApp) include(mitkFunctionCreateModule) include(mitkFunctionCreatePlugin) include(mitkFunctionCreateProvisioningFile) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionGetVersion) include(mitkFunctionGetVersionDescription) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionInstallCTKPlugin) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallThirdPartyCTKPlugins) include(mitkFunctionOrganizeSources) include(mitkFunctionTestPlugin) include(mitkFunctionUseModules) if( ${MITK_USE_MatchPoint} ) include(mitkFunctionCreateMatchPointDeployedAlgorithm) endif() include(mitkMacroConfigureItkPixelTypes) include(mitkMacroCreateExecutable) include(mitkMacroCreateModuleTests) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) include(mitkMacroGetPMDPlatformString) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroMultiplexPicType) # Deprecated include(mitkMacroCreateCTKPlugin) #----------------------------------------------------------------------------- # Global CMake variables #----------------------------------------------------------------------------- # Required and enabled C++14 features for all MITK code. # These are added as PUBLIC compile features to all MITK modules. set(MITK_CXX_FEATURES cxx_auto_type cxx_decltype cxx_enum_forward_declarations cxx_extended_friend_declarations cxx_extern_templates cxx_final cxx_lambdas cxx_local_type_template_args cxx_long_long_type cxx_nullptr cxx_override cxx_range_for cxx_right_angle_brackets cxx_rvalue_references cxx_static_assert cxx_strong_enums cxx_template_template_parameters cxx_trailing_return_types cxx_variadic_macros ) if(NOT DEFINED CMAKE_DEBUG_POSTFIX) # We can't do this yet because the CTK Plugin Framework # cannot cope with a postfix yet. #set(CMAKE_DEBUG_POSTFIX d) endif() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- set(_default_LIBRARY_output_dir lib) set(_default_RUNTIME_output_dir bin) set(_default_ARCHIVE_output_dir lib) foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) else() set(CMAKE_${type}_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${_default_${type}_output_dir}) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY}) endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY} CACHE INTERNAL "Output directory for ${type} files.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- # Look for optional Doxygen package find_package(Doxygen) option(BLUEBERRY_DEBUG_SMARTPOINTER "Enable code for debugging smart pointers" OFF) mark_as_advanced(BLUEBERRY_DEBUG_SMARTPOINTER) # ASK THE USER TO SHOW THE CONSOLE WINDOW FOR CoreApp and mitkWorkbench option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) # TODO: check if necessary option(USE_ITKZLIB "Use the ITK zlib for pic compression." ON) mark_as_advanced(USE_ITKZLIB) if(NOT MITK_FAST_TESTING) if(DEFINED MITK_CTEST_SCRIPT_MODE AND (MITK_CTEST_SCRIPT_MODE STREQUAL "continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "experimental") ) set(MITK_FAST_TESTING 1) endif() endif() if(NOT UNIX) set(MITK_WIN32_FORCE_STATIC "STATIC" CACHE INTERNAL "Use this variable to always build static libraries on non-unix platforms") endif() if(MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() # Configure pixel types used for ITK image access multiplexing mitkMacroConfigureItkPixelTypes() # Configure module naming conventions set(MITK_MODULE_NAME_REGEX_MATCH "^[A-Z].*$") set(MITK_MODULE_NAME_REGEX_NOT_MATCH "^[Mm][Ii][Tt][Kk].*$") set(MITK_DEFAULT_MODULE_NAME_PREFIX "Mitk") set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) set(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME 1) #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK) # MITK_VERSION set(MITK_VERSION_STRING "${MITK_VERSION_MAJOR}.${MITK_VERSION_MINOR}.${MITK_VERSION_PATCH}") if(MITK_VERSION_PATCH STREQUAL "99") set(MITK_VERSION_STRING "${MITK_VERSION_STRING}-${MITK_REVISION_SHORTID}") endif() #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on macOS all BlueBerry plugins get copied into every # application bundle (.app directory) specified here if(MITK_USE_BLUEBERRY AND APPLE) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Applications) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) set(MITK_APPS "") include(${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} Mitk${app_name}) endif() endforeach() endif() endforeach() endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}") set(MITK_C_FLAGS_DEBUG ) set(MITK_C_FLAGS_RELEASE ) set(MITK_CXX_FLAGS "${COVERAGE_CXX_FLAGS} ${MITK_CXX14_FLAG}") set(MITK_CXX_FLAGS_DEBUG ) set(MITK_CXX_FLAGS_RELEASE ) set(MITK_EXE_LINKER_FLAGS ) set(MITK_SHARED_LINKER_FLAGS ) find_package(OpenMP) if (OPENMP_FOUND) set(MITK_C_FLAGS "${MITK_C_FLAGS} ${OpenMP_C_FLAGS}") set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") endif() 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-array-bounds -fdiagnostics-show-option ) mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS) endforeach() endif() if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS) endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS) set(MITK_CXX_FLAGS_RELEASE "-U_FORTIFY_SOURCES -D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}") endif() set(MITK_MODULE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) set(MITK_EXE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_PACKAGE_DEPENDS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMake/PackageDepends) get_filename_component(MITK_PACKAGE_DEPENDS_EXTENSION_DIR ${MITK_PACKAGE_DEPENDS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}) list(APPEND MODULES_PACKAGE_DEPENDS_DIRS ${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}) endif() endforeach() if(NOT MITK_USE_SYSTEM_Boost) set(Boost_NO_SYSTEM_PATHS 1) endif() set(Boost_USE_MULTITHREADED 1) set(Boost_USE_STATIC_LIBS 0) set(Boost_USE_STATIC_RUNTIME 0) set(Boost_ADDITIONAL_VERSIONS 1.68 1.68.0) # We need this later for a DCMTK workaround set(_dcmtk_dir_orig ${DCMTK_DIR}) # This property is populated at the top half of this file get_property(MITK_EXTERNAL_PROJECTS GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(MITK_USE_${ep} AND _package) if(_components) find_package(${_package} COMPONENTS ${_components} REQUIRED CONFIG) else() # Prefer config mode first because it finds external # Config.cmake files pointed at by _DIR variables. # Otherwise, existing Find.cmake files could fail. if(DEFINED ${_package}_DIR) #we store the information because it will be overwritten by find_package #and would get lost for all EPs that use on Find.cmake instead of config #files. set(_temp_EP_${_package}_dir ${${_package}_DIR}) endif(DEFINED ${_package}_DIR) find_package(${_package} QUIET CONFIG) string(TOUPPER "${_package}" _package_uc) if(NOT (${_package}_FOUND OR ${_package_uc}_FOUND)) if(DEFINED _temp_EP_${_package}_dir) set(${_package}_DIR ${_temp_EP_${_package}_dir} CACHE PATH "externaly set dir of the package ${_package}" FORCE) endif(DEFINED _temp_EP_${_package}_dir) find_package(${_package} REQUIRED) endif() endif() endif() endforeach() # Ensure that the MITK CMake module path comes first set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR} ${CMAKE_MODULE_PATH} ) if(MITK_USE_DCMTK) # Due to the preferred CONFIG mode in find_package calls above, # the DCMTKConfig.cmake file is read, which does not provide useful # package information. We explictly need MODULE mode to find DCMTK. if(${_dcmtk_dir_orig} MATCHES "${MITK_EXTERNAL_PROJECT_PREFIX}.*") # Help our FindDCMTK.cmake script find our super-build DCMTK set(DCMTK_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) else() # Use the original value set(DCMTK_DIR ${_dcmtk_dir_orig}) endif() find_package(DCMTK REQUIRED MODULE) endif() if(MITK_USE_DCMQI) # Due to the preferred CONFIG mode in find_package calls above, # the DCMQIConfig.cmake file is read, which does not provide useful # package information. We explictly need MODULE mode to find DCMQI. # Help our FindDCMQI.cmake script find our super-build DCMQI set(DCMQI_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) find_package(DCMQI REQUIRED) endif() link_directories(${Boost_LIBRARY_DIRS}) if(MITK_USE_OpenIGTLink) link_directories(${OpenIGTLink_LIBRARY_DIRS}) endif() if(MITK_USE_OpenCL) find_package(OpenCL REQUIRED) endif() # Qt support if(MITK_USE_Qt5) find_package(Qt5Core ${MITK_QT5_MINIMUM_VERSION} REQUIRED) # at least Core required get_target_property(_qmake_exec Qt5::qmake LOCATION) execute_process(COMMAND ${_qmake_exec} -query QT_INSTALL_BINS RESULT_VARIABLE _result OUTPUT_VARIABLE QT_BINARY_DIR ERROR_VARIABLE _error ) string(STRIP "${QT_BINARY_DIR}" QT_BINARY_DIR) if(_result OR NOT EXISTS "${QT_BINARY_DIR}") message(FATAL_ERROR "Could not determine Qt binary directory: ${_result} ${QT_BINARY_DIR} ${_error}") endif() find_program(QT_HELPGENERATOR_EXECUTABLE NAMES qhelpgenerator qhelpgenerator-qt5 qhelpgenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_COLLECTIONGENERATOR_EXECUTABLE NAMES qcollectiongenerator qcollectiongenerator-qt5 qcollectiongenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_ASSISTANT_EXECUTABLE NAMES assistant assistant-qt5 assistant5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_XMLPATTERNS_EXECUTABLE NAMES xmlpatterns PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) mark_as_advanced(QT_HELPGENERATOR_EXECUTABLE QT_COLLECTIONGENERATOR_EXECUTABLE QT_ASSISTANT_EXECUTABLE QT_XMLPATTERNS_EXECUTABLE ) if(MITK_USE_BLUEBERRY) option(BLUEBERRY_USE_QT_HELP "Enable support for integrating plugin documentation into Qt Help" ${DOXYGEN_FOUND}) mark_as_advanced(BLUEBERRY_USE_QT_HELP) # Sanity checks for in-application BlueBerry plug-in help generation if(BLUEBERRY_USE_QT_HELP) set(_force_blueberry_use_qt_help_to_off 0) if(NOT DOXYGEN_FOUND) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen was not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_LESS 1.8.7) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_HELPGENERATOR_EXECUTABLE) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because QT_HELPGENERATOR_EXECUTABLE is empty.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT MITK_USE_Qt5) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt5 is OFF.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_XMLPATTERNS_EXECUTABLE) message("You have enabled Qt Help support, but QT_XMLPATTERNS_EXECUTABLE is empty") set(_force_blueberry_use_qt_help_to_off 1) endif() if(_force_blueberry_use_qt_help_to_off) set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating plugin documentation into Qt Help" FORCE) endif() endif() if(BLUEBERRY_QT_HELP_REQUIRED AND NOT BLUEBERRY_USE_QT_HELP) message(FATAL_ERROR "BLUEBERRY_USE_QT_HELP is required to be set to ON") endif() endif() endif() #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) enable_testing() include(CTest) mark_as_advanced(TCL_TCLSH DART_ROOT) option(MITK_ENABLE_RENDERING_TESTING OFF "Enable the MITK rendering tests. Requires x-server in Linux.") #Rendering testing does not work for Linux nightlies, thus it is disabled per default #and activated for Mac and Windows. if(WIN32 OR APPLE) set(MITK_ENABLE_RENDERING_TESTING ON) endif() mark_as_advanced( MITK_ENABLE_RENDERING_TESTING ) # Setup file for setting custom ctest vars configure_file( CMake/CTestCustom.cmake.in ${MITK_BINARY_DIR}/CTestCustom.cmake @ONLY ) # Initial cache for ProjectTemplate and PluginGenerator tests configure_file( CMake/mitkTestInitialCache.txt.in ${MITK_BINARY_DIR}/mitkTestInitialCache.txt @ONLY ) # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch( std::exception & excp ) { fprintf(stderr,\"%s\\n\",excp.what()); return EXIT_FAILURE; } catch( ... ) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; } ") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the external project template if(MITK_USE_BLUEBERRY) include(mitkTestProjectTemplate) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Set C/CXX and linker flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MITK_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MITK_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MITK_C_FLAGS_RELEASE}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MITK_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MITK_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MITK_MODULE_LINKER_FLAGS}") #----------------------------------------------------------------------------- # Add custom targets representing CDash subprojects #----------------------------------------------------------------------------- foreach(subproject ${CTEST_PROJECT_SUBPROJECTS}) if(NOT TARGET ${subproject} AND NOT subproject MATCHES "Unlabeled") add_custom_target(${subproject}) endif() endforeach() #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- add_subdirectory(Utilities) add_subdirectory(Modules) include("${CMAKE_CURRENT_SOURCE_DIR}/Modules/ModuleList.cmake") mitkFunctionWhitelistModules(MITK MITK_MODULES) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_MODULES_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Modules) get_filename_component(MITK_MODULES_EXTENSION_DIR ${MITK_MODULES_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake) set(MITK_MODULES "") include(${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake) foreach(mitk_module ${MITK_MODULES}) add_subdirectory(${MITK_MODULES_EXTENSION_DIR}/${mitk_module} Modules/${mitk_module}) endforeach() endif() set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) endforeach() add_subdirectory(Wrapping) if(MITK_USE_BLUEBERRY) set(BLUEBERRY_XPDOC_OUTPUT_DIR ${MITK_DOXYGEN_OUTPUT_DIR}/html/extension-points/html/) # Plug-in testing (needs some work to be enabled again) if(BUILD_TESTING) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp") if(TARGET CoreApp) get_target_property(_is_macosx_bundle CoreApp MACOSX_BUNDLE) if(APPLE AND _is_macosx_bundle) set(BLUEBERRY_UI_TEST_APP "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CoreApp.app/Contents/MacOS/CoreApp") endif() endif() set(BLUEBERRY_TEST_APP_ID "org.mitk.qt.coreapplication") endif() include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") mitkFunctionWhitelistPlugins(MITK MITK_PLUGINS) set(mitk_plugins_fullpath "") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() set(MITK_PLUGIN_REGEX_LIST "") foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_PLUGINS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Plugins) get_filename_component(MITK_PLUGINS_EXTENSION_DIR ${MITK_PLUGINS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake) set(MITK_PLUGINS "") include(${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake) foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PLUGINS_EXTENSION_DIR}/${mitk_plugin}) endforeach() endif() endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() if(MITK_BUILD_EXAMPLES) include("${CMAKE_CURRENT_SOURCE_DIR}/Examples/Plugins/PluginList.cmake") set(mitk_example_plugins_fullpath ) foreach(mitk_example_plugin ${MITK_EXAMPLE_PLUGINS}) list(APPEND mitk_example_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) list(APPEND mitk_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb MITK_PLUGIN_REGEX_LIST OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options set(mitk_apps_fullpath "") foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Applications) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) set(MITK_APPS "") include(${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) if(${option_name}) list(APPEND mitk_apps_fullpath "${MITK_APPLICATIONS_EXTENSION_DIR}/${directory_name}^^${option_name}") endif() endforeach() endif() endforeach() if (mitk_plugins_fullpath) ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) endif() set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- if(DOXYGEN_FOUND) add_subdirectory(Documentation) endif() #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) set(ALL_MITK_APPS "") set(activated_apps_no 0) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Applications) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) set(MITK_APPS "") include(${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) foreach(mitk_app ${MITK_APPS}) string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) list(APPEND ALL_MITK_APPS "${MITK_EXTENSION_DIR}/Applications/${directory_name}^^${option_name}^^${executable_name}") if(${option_name} OR MITK_BUILD_ALL_APPS) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() endif() endforeach() list(LENGTH ALL_MITK_APPS app_count) if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${ALL_MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${target_dir}/CPackOptions.cmake") include("${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${target_dir}/CPackConfig.cmake") configure_file(${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${executable_name}") endif() endforeach() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- # ---------------- Export targets ----------------- set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) set(targets_to_export) get_property(module_targets GLOBAL PROPERTY MITK_MODULE_TARGETS) if(module_targets) list(APPEND targets_to_export ${module_targets}) endif() if(MITK_USE_BLUEBERRY) if(MITK_PLUGIN_LIBRARIES) list(APPEND targets_to_export ${MITK_PLUGIN_LIBRARIES}) endif() endif() export(TARGETS ${targets_to_export} APPEND FILE ${MITK_EXPORTS_FILE}) set(MITK_EXPORTED_TARGET_PROPERTIES ) foreach(target_to_export ${targets_to_export}) get_target_property(autoload_targets ${target_to_export} MITK_AUTOLOAD_TARGETS) if(autoload_targets) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_TARGETS \"${autoload_targets}\")") endif() get_target_property(autoload_dir ${target_to_export} MITK_AUTOLOAD_DIRECTORY) if(autoload_dir) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_DIRECTORY \"${autoload_dir}\")") endif() get_target_property(deprecated_module ${target_to_export} MITK_MODULE_DEPRECATED_SINCE) if(deprecated_module) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_MODULE_DEPRECATED_SINCE \"${deprecated_module}\")") endif() endforeach() # ---------------- External projects ----------------- get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) set(MITK_CONFIG_EXTERNAL_PROJECTS ) #string(REPLACE "^^" ";" _mitk_external_projects ${MITK_EXTERNAL_PROJECTS}) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} set(MITK_USE_${ep} ${MITK_USE_${ep}}) set(MITK_${ep}_DIR \"${${ep}_DIR}\") set(MITK_${ep}_COMPONENTS ${_components}) ") endforeach() foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(_components) set(_components_arg COMPONENTS \${_components}) else() set(_components_arg) endif() if(_package) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} if(MITK_USE_${ep}) set(${ep}_DIR \${MITK_${ep}_DIR}) if(MITK_${ep}_COMPONENTS) mitkMacroFindDependency(${_package} COMPONENTS \${MITK_${ep}_COMPONENTS}) else() mitkMacroFindDependency(${_package}) endif() endif()") endif() endforeach() # ---------------- Tools ----------------- configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) # ---------------- Configure files ----------------- configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake VERSION ${MITK_VERSION_STRING} COMPATIBILITY AnyNewerVersion) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) if(MSVC AND TARGET MitkWorkbench) set_directory_properties(PROPERTIES VS_STARTUP_PROJECT MitkWorkbench) endif() foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Applications) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_APPLICATIONS_EXTENSION_DIR}/CMakeLists.txt) add_subdirectory(${MITK_APPLICATIONS_EXTENSION_DIR} Applications) endif() endforeach() #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) endif() #----------------------------------------------------------------------------- # Print configuration summary #----------------------------------------------------------------------------- message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL ) diff --git a/Modules/AlgorithmsExt/files.cmake b/Modules/AlgorithmsExt/files.cmake index 55da8aeb07..3e8bacd12d 100644 --- a/Modules/AlgorithmsExt/files.cmake +++ b/Modules/AlgorithmsExt/files.cmake @@ -1,36 +1,37 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkAutoCropImageFilter.cpp mitkBoundingObjectCutter.cpp mitkBoundingObjectToSegmentationFilter.cpp mitkGeometryClipImageFilter.cpp mitkGeometryDataSource.cpp mitkHeightFieldSurfaceClipImageFilter.cpp mitkImageToUnstructuredGridFilter.cpp mitkLabeledImageToSurfaceFilter.cpp mitkMaskAndCutRoiImageFilter.cpp mitkMaskImageFilter.cpp mitkMovieGenerator.cpp mitkNonBlockingAlgorithm.cpp mitkPadImageFilter.cpp mitkPlaneFit.cpp mitkPlaneLandmarkProjector.cpp mitkPointLocator.cpp mitkSegmentationSink.cpp mitkSimpleHistogram.cpp mitkSimpleUnstructuredGridHistogram.cpp mitkCovarianceMatrixCalculator.cpp mitkAnisotropicIterativeClosestPointRegistration.cpp mitkWeightedPointTransform.cpp mitkAnisotropicRegistrationCommon.cpp mitkUnstructuredGridClusteringFilter.cpp mitkUnstructuredGridToUnstructuredGridFilter.cpp mitkSurfaceToPointSetFilter.cpp + mitkCropTimestepsImageFilter.cpp ) if(WIN32) list(APPEND CPP_FILES mitkMovieGeneratorWin32.cpp ) endif() diff --git a/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h b/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h new file mode 100644 index 0000000000..f2cbe5496b --- /dev/null +++ b/Modules/AlgorithmsExt/include/mitkCropTimestepsImageFilter.h @@ -0,0 +1,84 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#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 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/src/mitkCropTimestepsImageFilter.cpp b/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp new file mode 100644 index 0000000000..c68dffe8b7 --- /dev/null +++ b/Modules/AlgorithmsExt/src/mitkCropTimestepsImageFilter.cpp @@ -0,0 +1,151 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#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); + newTimeGeometry->AppendNewTimeStep(geometryForTimePoint, + sourceGeometry->GetMinimumTimePoint(timestep), + sourceGeometry->GetMaximumTimePoint(timestep)); + } + 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() +{ + Superclass::VerifyInputInformation(); + + VerifyInputImage(this->GetInput()); +} diff --git a/Modules/AlgorithmsExt/test/files.cmake b/Modules/AlgorithmsExt/test/files.cmake index 06daaeba52..3701cf8540 100644 --- a/Modules/AlgorithmsExt/test/files.cmake +++ b/Modules/AlgorithmsExt/test/files.cmake @@ -1,15 +1,16 @@ set(MODULE_TESTS mitkAutoCropImageFilterTest.cpp mitkBoundingObjectCutterTest.cpp mitkImageToUnstructuredGridFilterTest.cpp mitkPlaneFitTest.cpp mitkSimpleHistogramTest.cpp mitkCovarianceMatrixCalculatorTest.cpp mitkAnisotropicIterativeClosestPointRegistrationTest.cpp mitkUnstructuredGridClusteringFilterTest.cpp mitkUnstructuredGridToUnstructuredGridFilterTest.cpp + mitkCropTimestepsImageFilterTest.cpp ) set(MODULE_CUSTOM_TESTS mitkLabeledImageToSurfaceFilterTest.cpp ) diff --git a/Modules/AlgorithmsExt/test/mitkCropTimestepsImageFilterTest.cpp b/Modules/AlgorithmsExt/test/mitkCropTimestepsImageFilterTest.cpp new file mode 100644 index 0000000000..e9d6c2e18c --- /dev/null +++ b/Modules/AlgorithmsExt/test/mitkCropTimestepsImageFilterTest.cpp @@ -0,0 +1,181 @@ +/*=================================================================== +The Medical Imaging Interaction Toolkit (MITK) +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. +See LICENSE.txt or http://www.mitk.org for details. +===================================================================*/ +// Testing +#include "mitkTestingMacros.h" +#include "mitkTestFixture.h" + +//MITK includes +#include +#include + +class mitkCropTimestepsImageFilterTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkCropTimestepsImageFilterTestSuite); + MITK_TEST(Constructor_Null); + MITK_TEST(Constructor_NoTime); + MITK_TEST(Setter_UpperBoundaryTimestepSmallerThanLowerBoundaryTimestep); + MITK_TEST(Setter_UpperTimestepsGreaterThanMaxImageTimestep); + MITK_TEST(Filter_Default); + MITK_TEST(Filter_OnlyLowerBoundary); + MITK_TEST(Filter_OnlyUpperBoundary); + MITK_TEST(Filter_BothBoundaries); + MITK_TEST(Filter_BothBoundaries2Dt); + CPPUNIT_TEST_SUITE_END(); +private: + std::string m_ImageFilename2D, m_ImageFilename3D, m_ImageFilename2Dt, m_ImageFilename3Dt; + mitk::CropTimestepsImageFilter::Pointer m_cropTimestepsFilter; +public: + void setUp() override + { + m_ImageFilename2D = GetTestDataFilePath("Png2D-bw.png"); + m_ImageFilename3D = GetTestDataFilePath("Pic3D.nrrd"); + m_ImageFilename2Dt = GetTestDataFilePath("Pic2DplusT.nrrd"); + m_ImageFilename3Dt = GetTestDataFilePath("3D+t-ITKIO-TestData/LinearModel_4D_arbitrary_time_geometry.nrrd"); + m_cropTimestepsFilter = mitk::CropTimestepsImageFilter::New(); + } + + void tearDown() override + { + } + + void Constructor_Null() + { + m_cropTimestepsFilter->SetInput(nullptr); + CPPUNIT_ASSERT_THROW(m_cropTimestepsFilter->Update(), itk::ExceptionObject); + } + + void Constructor_NoTime() + { + auto refImage2D = mitk::IOUtil::Load(m_ImageFilename2D); + auto refImage3D = mitk::IOUtil::Load(m_ImageFilename3D); + m_cropTimestepsFilter->SetInput(refImage2D); + CPPUNIT_ASSERT_THROW(m_cropTimestepsFilter->Update(), mitk::Exception); + m_cropTimestepsFilter->SetInput(refImage3D); + CPPUNIT_ASSERT_THROW(m_cropTimestepsFilter->Update(), mitk::Exception); + } + + void Setter_UpperBoundaryTimestepSmallerThanLowerBoundaryTimestep() + { + //check for Exception if UpperBoundaryTimestep < LowerBoundaryTimestep + auto refImage2Dt = mitk::IOUtil::Load(m_ImageFilename2Dt); + m_cropTimestepsFilter->SetInput(refImage2Dt); + unsigned int lowerTimeStep = 5; + unsigned int upperTimeStep = 4; + m_cropTimestepsFilter->SetLowerBoundaryTimestep(lowerTimeStep); + m_cropTimestepsFilter->SetUpperBoundaryTimestep(upperTimeStep); + CPPUNIT_ASSERT_EQUAL(m_cropTimestepsFilter->GetLowerBoundaryTimestep(), lowerTimeStep); + CPPUNIT_ASSERT_EQUAL(m_cropTimestepsFilter->GetUpperBoundaryTimestep(), upperTimeStep); + CPPUNIT_ASSERT_THROW(m_cropTimestepsFilter->Update(), mitk::Exception); + } + + void Setter_UpperTimestepsGreaterThanMaxImageTimestep() + { + //check for correction if UpperBoundaryTimestep > image->GetTimestep() + auto refImage2Dt = mitk::IOUtil::Load(m_ImageFilename2Dt); + m_cropTimestepsFilter->SetInput(refImage2Dt); + unsigned int upperTimeStep = 11; + m_cropTimestepsFilter->SetUpperBoundaryTimestep(upperTimeStep); + CPPUNIT_ASSERT_EQUAL(m_cropTimestepsFilter->GetUpperBoundaryTimestep(), upperTimeStep); + CPPUNIT_ASSERT_NO_THROW(m_cropTimestepsFilter->Update()); + CPPUNIT_ASSERT_EQUAL(m_cropTimestepsFilter->GetUpperBoundaryTimestep(), refImage2Dt->GetTimeSteps()); + } + + void Filter_Default() + { + //Everything default: timesteps should stay the same + auto refImage3Dt = mitk::IOUtil::Load(m_ImageFilename3Dt); + auto timeGeometryBefore = refImage3Dt->GetTimeGeometry()->Clone(); + m_cropTimestepsFilter->SetInput(refImage3Dt); + unsigned int expectedLowerTimestep = 0; + unsigned int expectedUpperTimestep = std::numeric_limits::max(); + CPPUNIT_ASSERT_EQUAL(m_cropTimestepsFilter->GetLowerBoundaryTimestep(), expectedLowerTimestep); + CPPUNIT_ASSERT_EQUAL(m_cropTimestepsFilter->GetUpperBoundaryTimestep(), expectedUpperTimestep); + CPPUNIT_ASSERT_NO_THROW(m_cropTimestepsFilter->Update()); + CPPUNIT_ASSERT_EQUAL(m_cropTimestepsFilter->GetLowerBoundaryTimestep(), expectedLowerTimestep); + CPPUNIT_ASSERT_EQUAL(m_cropTimestepsFilter->GetUpperBoundaryTimestep(), refImage3Dt->GetTimeSteps()); + auto result = m_cropTimestepsFilter->GetOutput(); + auto timeGeometry = result->GetTimeGeometry(); + CPPUNIT_ASSERT_EQUAL(timeGeometryBefore->GetMinimumTimePoint(), timeGeometry->GetMinimumTimePoint()); + CPPUNIT_ASSERT_EQUAL(timeGeometryBefore->GetMaximumTimePoint(), timeGeometry->GetMaximumTimePoint()); + + CPPUNIT_ASSERT_EQUAL(result->GetTimeSteps(), refImage3Dt->GetTimeSteps()); + } + + void Filter_OnlyLowerBoundary() + { + //Crop lower 2 timesteps + auto refImage3Dt = mitk::IOUtil::Load(m_ImageFilename3Dt); + MITK_WARN << "before: " << *refImage3Dt; + auto timeGeometryBefore = refImage3Dt->GetTimeGeometry()->Clone(); + m_cropTimestepsFilter->SetInput(refImage3Dt); + unsigned int lowerTimestep = 2; + m_cropTimestepsFilter->SetLowerBoundaryTimestep(lowerTimestep); + CPPUNIT_ASSERT_NO_THROW(m_cropTimestepsFilter->Update()); + auto result = m_cropTimestepsFilter->GetOutput(); + auto timeGeometry = result->GetTimeGeometry(); + CPPUNIT_ASSERT_EQUAL(timeGeometryBefore->TimeStepToTimePoint(lowerTimestep), timeGeometry->GetMinimumTimePoint()); + CPPUNIT_ASSERT_EQUAL(timeGeometryBefore->GetMaximumTimePoint(), timeGeometry->GetMaximumTimePoint()); + CPPUNIT_ASSERT_EQUAL(refImage3Dt->GetTimeSteps()-lowerTimestep, result->GetTimeSteps()); + } + + void Filter_OnlyUpperBoundary() + { + // Crop upper 3 timesteps + auto refImage3Dt = mitk::IOUtil::Load(m_ImageFilename3Dt); + auto timeGeometryBefore = refImage3Dt->GetTimeGeometry()->Clone(); + m_cropTimestepsFilter->SetInput(refImage3Dt); + unsigned int upperTimestep = 7; + m_cropTimestepsFilter->SetUpperBoundaryTimestep(upperTimestep); + CPPUNIT_ASSERT_NO_THROW(m_cropTimestepsFilter->Update()); + auto result = m_cropTimestepsFilter->GetOutput(); + auto timeGeometry = result->GetTimeGeometry(); + CPPUNIT_ASSERT_EQUAL(timeGeometryBefore->TimeStepToTimePoint(upperTimestep), timeGeometry->GetMaximumTimePoint()); + CPPUNIT_ASSERT_EQUAL(timeGeometryBefore->GetMinimumTimePoint(), timeGeometry->GetMinimumTimePoint()); + CPPUNIT_ASSERT_EQUAL(upperTimestep, result->GetTimeSteps()); + } + + void Filter_BothBoundaries() + { + //Crop lower 2 and upper 3 timesteps + auto refImage3Dt = mitk::IOUtil::Load(m_ImageFilename3Dt); + m_cropTimestepsFilter->SetInput(refImage3Dt); + auto timeGeometryBefore = refImage3Dt->GetTimeGeometry()->Clone(); + unsigned int lowerTimestep = 1; + unsigned int upperTimestep = 7; + m_cropTimestepsFilter->SetLowerBoundaryTimestep(lowerTimestep); + m_cropTimestepsFilter->SetUpperBoundaryTimestep(upperTimestep); + CPPUNIT_ASSERT_NO_THROW(m_cropTimestepsFilter->Update()); + auto result = m_cropTimestepsFilter->GetOutput(); + auto timeGeometry = result->GetTimeGeometry(); + CPPUNIT_ASSERT_EQUAL(timeGeometryBefore->TimeStepToTimePoint(upperTimestep), timeGeometry->GetMaximumTimePoint()); + CPPUNIT_ASSERT_EQUAL(timeGeometryBefore->TimeStepToTimePoint(lowerTimestep), timeGeometry->GetMinimumTimePoint()); + CPPUNIT_ASSERT_EQUAL(upperTimestep-lowerTimestep, result->GetTimeSteps()); + } + + void Filter_BothBoundaries2Dt() + { + //Crop lower 1 and upper 1 timestep, resulting in 1 remaining timestep (2D+t input) + auto refImage2Dt = mitk::IOUtil::Load(m_ImageFilename2Dt); + m_cropTimestepsFilter->SetInput(refImage2Dt); + unsigned int lowerTimestep = 1; + unsigned int upperTimestep = 2; + m_cropTimestepsFilter->SetLowerBoundaryTimestep(lowerTimestep); + m_cropTimestepsFilter->SetUpperBoundaryTimestep(upperTimestep); + CPPUNIT_ASSERT_NO_THROW(m_cropTimestepsFilter->Update()); + auto result = m_cropTimestepsFilter->GetOutput(); + auto timeGeometry = result->GetTimeGeometry(); + CPPUNIT_ASSERT_EQUAL(mitk::TimePointType(upperTimestep), timeGeometry->GetMaximumTimePoint()); + CPPUNIT_ASSERT_EQUAL(mitk::TimePointType(lowerTimestep), timeGeometry->GetMinimumTimePoint()); + CPPUNIT_ASSERT_EQUAL(upperTimestep - lowerTimestep, result->GetTimeSteps()); + } + +}; +MITK_TEST_SUITE_REGISTRATION(mitkCropTimestepsImageFilter) diff --git a/Modules/AppUtil/CMakeLists.txt b/Modules/AppUtil/CMakeLists.txt index 8294975eec..579fad9f59 100644 --- a/Modules/AppUtil/CMakeLists.txt +++ b/Modules/AppUtil/CMakeLists.txt @@ -1,8 +1,14 @@ +set(qt5_depends Qt5|Widgets) + +if(UNIX AND NOT APPLE) + set(qt5_depends "${qt5_depends}+X11Extras") +endif() + mitk_create_module( PACKAGE_DEPENDS - PUBLIC CTK|CTKPluginFramework Qt5|Widgets Poco|Util + PUBLIC CTK|CTKPluginFramework ${qt5_depends} Poco|Util PRIVATE VTK DEPENDS PUBLIC qtsingleapplication PRIVATE MitkCore ) diff --git a/Modules/Chart/include/QmitkChartWidget.h b/Modules/Chart/include/QmitkChartWidget.h index 94b95a66e6..dea90f4365 100644 --- a/Modules/Chart/include/QmitkChartWidget.h +++ b/Modules/Chart/include/QmitkChartWidget.h @@ -1,268 +1,273 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkC3jsWidget_h #define QmitkC3jsWidget_h #include #include #include #include /*! \brief QmitkChartWidget is a widget to display various charts based on the javascript chart library plotly. * \details Data is added via AddData1D() or AddData2D().\n * There can be multiple charts (of different types with different properties) created by calling AddData1D or AddData2D multiple times.\n\n * The following chart types are supported: * * line chart * * bar chart * * spline chart * * pie chart * * scatter chart * * area chart * * area spline chart. * * Technical details: The javascript code is embedded in a QWebEngineView. The actual js code is implemented in resource\Chart.js. * \sa https://plot.ly/javascript/ for further information about the used javaScript library. * \ingroup Modules/Chart */ class MITKCHART_EXPORT QmitkChartWidget : public QWidget { Q_OBJECT public: /*! * \brief enum of diagram types. */ enum class ChartType { bar, /*!< bar chart, see https://plot.ly/javascript/bar-charts/ */ line, /*!< line chart, see https://plot.ly/javascript/line-charts/ */ spline, /*!< spline chart (smoothed line chart), see https://plot.ly/~jduelfer/23/spline/#/ */ pie, /*!< pie chart, see https://plot.ly/javascript/pie-charts/ */ area, /*!< area chart, see https://plot.ly/javascript/filled-area-plots/ */ area_spline, /*!< area-spline chart, similar to https://plot.ly/~jduelfer/23/spline/#/ */ scatter /*!< scatter chart, see https://plot.ly/javascript/line-and-scatter/ */ }; /*! * \brief enum of chart style (modifies background and line color). */ enum class ColorTheme { darkstyle, /*!< background color: dark gray, foreground color: white*/ lightstyle /*!< background color: white, foreground color: black */ }; enum class LineStyle { solid, dashed }; enum class AxisScale { linear, log }; /*! * \brief enum of legend position. * See https://plot.ly/javascript/legend/ */ enum class LegendPosition { bottomMiddle, bottomRight, topRight, topLeft, middleRight }; explicit QmitkChartWidget(QWidget* parent = nullptr); //for UnitTests explicit QmitkChartWidget(QWidget *parent, bool unitTest); ~QmitkChartWidget() override; /*! * \brief Adds 1D data to the widget * \details internally, the list is converted to a map with increasing integers keys starting at 0. * \param label the name of the data that is also used as identifier. * \param chartType the chart type that should be used for this data entry * \note the data can be cleared with ClearDiagram() * \note If the label name already exists, the name is replaced with a unique one by concatenating numbers to it. * \warning Pie chart is significantly different than the other chart types. Here, the data given by AddData1D is summed. Each entry represents a different category. */ void AddData1D(const std::vector& data1D, const std::string& label, ChartType chartType = ChartType::bar); /*! * \brief Adds 2D data to the widget. Call repeatedly for displaying multiple charts. * \details each entry represents a data point: key: value --> x-value: y-value. * \param label the name of the data that is also used as identifier. * \param chartType the chart type that should be used for this data entry * \note the data can be cleared with ClearDiagram() * \note If the label name already exists, the name is replaced with a unique one by concatenating numbers to it. * \warning Pie chart is significantly different than the other chart types. Here, the data given by AddData1D is summed. Each entry represents a different category. */ void AddData2D(const std::map &data2D, const std::string &label, ChartType chartType = ChartType::bar); /*! * \brief Removes data from the widget, works for 1D and 2D Data * \param label the name of the data that is also used as identifier. * \note All data can be cleared with ClearDiagram() * \throws Invalid Argument Exception when the label cannot be found */ void RemoveData(const std::string& label); /*! * \brief Sets the color of one data entry (identifier is previously assigned label) * \details the color name can be "red" or a hex number (#FF0000). * \warning Either define all data entries with a color or no data entry. If a mixed approach is used, * plotly choses the color of the data entry (that could be the same as a user defined color). * \note If an unknown label is given, nothing happens. * \sa https://www.w3schools.com/cssref/css_colors.asp */ void SetColor(const std::string& label, const std::string& colorName); /*! * \brief Sets the line style of one data entry (identifier is previously assigned label) * \details two line styles are possible: LineStyle::solid and LineStyle::dashed. * The default line style is solid. * \note If an unknown label is given, nothing happens. * \warning only sets the line style if the current chart type is ChartType::line. * However, the line style remains also if the chart changes (e.g. new chart type) */ void SetLineStyle(const std::string& label, LineStyle style); /*! * \brief Sets the axis scale to either linear (default) or logarithmic. * \sa https://plot.ly/javascript/log-plot/ */ void SetYAxisScale(AxisScale scale); void SetXAxisLabel(const std::string& label); void SetYAxisLabel(const std::string& label); /*! * \brief Sets a title for the chart. */ void SetTitle(const std::string &title); /*! * \brief Changes the chart type for all data entries and reloads the chart */ void SetChartTypeForAllDataAndReload(ChartType type); /*! * \brief Sets the chart type for a data entry * \details for available types, see ChartType * \note If an unknown label is given, nothing happens. * \warning Pie chart is significantly different than the other chart types. Here, the data given by AddData1D is summed. Each entry represents a different category. * \sa DiagramType for available types */ void SetChartType(const std::string& label, ChartType type); /*! * \brief Sets error bars for data in x direction * \note If only error plus is provided, the error bars are symmetrical * \param label the name of the data that is also used as identifier. * \param errorPlus the error in positive direction * \param errorMinus the error in negative direction. Same as error plus if omitted */ void SetXErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector& errorMinus = std::vector()); /*! * \brief Sets error bars for data in y direction * \details for parameters, see SetXErrorBars * \note If only error plus is provided, the error bars are symmetrical */ void SetYErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus = std::vector()); /*! * \brief Sets the legend position. * \details Default position is bottom. * \sa LegendPosition for available types */ void SetLegendPosition(LegendPosition position); void SetShowLegend(bool show); void SetStackedData(bool stacked); /*! * \brief Displays the chart in the widget * \param showSubChart if a subchart is displayed inside the widget or not. * \note if no data has been provided, (\sa AddData1D AddData2D), an empty chart is displayed. */ void Show(bool showSubChart=false); /*! * \brief Either displays the dataPoints or not * \param showDataPoints if dataPoints are displayed inside the widget or not. * \details: example for not showing points: https://plot.ly/javascript/line-charts/#styling-line-plot * example for showing the points: https://plot.ly/javascript/pointcloud/ */ void SetShowDataPoints(bool showDataPoints); /*! * \brief Clears all data inside and resets the widget. */ void Clear(); /*! * \brief Sets the theme of the widget. * \details default is dark theme as in MITK. * \warning has to be called before Show() or Reload() to work */ void SetTheme(ColorTheme themeEnabled); /*! * \brief Sets whether the subchart shall be shown. * \details Changes the state of the current chart object. * \note Needs to be reloaded with Reload() to display changes. */ void SetShowSubchart(bool showSubChart); /*! * \brief Sets whether the error bars shall be shown. * \details Changes the state of the current chart object. * \note Needs to be reloaded with Reload() to display changes. * \param showErrorBars if error bars are displayed or not. */ void SetShowErrorBars(bool showErrorBars); /*! - * \brief Updates the histogram´s min and max values - * \details Zooms in to view the values between minValue and maxValue + * \brief Updates the min and max x values of the chart + * \details Zooms in to view the values between minValue and maxValue in x direction */ void UpdateMinMaxValueXView(double minValueX,double maxValueX); + /*! + * \brief Updates the min and max y values of the chart + * \details Zooms in to view the values between minValue and maxValue in y direction + */ + void UpdateMinMaxValueYView(double minValueY, double maxValueY); /*! * \brief Reloads the chart in the widget * \details reloading may be needed to display added data in an existing chart */ void Reload(); QSize sizeHint() const override; public slots: void OnLoadFinished(bool isLoadSuccessful); void OnPageSuccessfullyLoaded(); signals: void PageSuccessfullyLoaded(); private: /*! source: https://stackoverflow.com/questions/29383/converting-bool-to-text-in-c*/ std::string convertBooleanValue(bool value) const; class Impl; std::unique_ptr m_Impl; }; #endif diff --git a/Modules/Chart/resource/Chart.js b/Modules/Chart/resource/Chart.js index 85d9ba60f9..0a0fb91356 100644 --- a/Modules/Chart/resource/Chart.js +++ b/Modules/Chart/resource/Chart.js @@ -1,405 +1,420 @@ document.body.style.backgroundColor = 'rgb(240, 240, 240)'; const minHeight = 255; var chart; var chartData; var xErrorValuesPlus=[]; var xErrorValuesMinus=[]; var yErrorValuesPlus=[]; var yErrorValuesMinus=[]; var xValues=[]; var yValues=[]; var dataLabels=[]; var xs = {}; var dataColors = {}; var chartTypes = {}; var lineStyle = {}; var backgroundColor = '#f0f0f0'; var foregroundColor = 'black'; var dataProperties = {}; // Important loading function. This will be executed at first in this whole script. // Fetching data from QWebChannel and storing them for display purposes. window.onload = function() { initHeight(); new QWebChannel(qt.webChannelTransport, function(channel) { chartData = channel.objects.chartData; let count = 0; for(let propertyName in channel.objects) { if (propertyName != 'chartData') { let xDataTemp = channel.objects[propertyName].m_XData; let yDataTemp = channel.objects[propertyName].m_YData; let xErrorsTempPlus = channel.objects[propertyName].m_XErrorDataPlus; let xErrorsTempMinus = channel.objects[propertyName].m_XErrorDataMinus; let yErrorsTempPlus = channel.objects[propertyName].m_YErrorDataPlus; let yErrorsTempMinus = channel.objects[propertyName].m_YErrorDataMinus; let dataLabel = channel.objects[propertyName].m_Label; dataLabels.push(dataLabel); console.log("loading datalabel: "+dataLabel); //add label to x array xDataTemp.unshift('x'+count.toString()) xs[dataLabel] = 'x' + count.toString() xDataTemp.push(null); //append null value, to make sure the last tick on x-axis is displayed correctly yDataTemp.unshift(dataLabel) yDataTemp.push(null); //append null value, to make sure the last tick on y-axis is displayed correctly xValues[count] = xDataTemp yValues[count] = yDataTemp xErrorValuesPlus[count] = xErrorsTempPlus; xErrorValuesMinus[count] = xErrorsTempMinus; yErrorValuesPlus[count] = yErrorsTempPlus; yErrorValuesMinus[count] = yErrorsTempMinus; var tempLineStyle = ''; if (channel.objects[propertyName].m_LineStyleName == "solid") { tempLineStyle = '' } else { tempLineStyle = "dashed" } dataProperties[dataLabel] = { "color" : channel.objects[propertyName].m_Color, "chartType": channel.objects[propertyName].m_ChartType, "style": tempLineStyle } count++; } } var theme = chartData.m_themeName; setThemeColors(theme); generateChart(chartData); }); } /** * Inits the height of the chart element to 90% of the full window height. */ function initHeight() { var size = window.innerHeight-(window.innerHeight/100*5); //subtract 10% of height to hide vertical scrool bar let chart = document.getElementById("chart"); chart.style.height = `${size}px`; } function getPlotlyChartType(inputType){ let plotlyType = inputType; if (inputType == "line"){ plotlyType = "scatter"; } else if (inputType == "scatter"){ plotlyType = "scatterOnly" } return plotlyType; } /** * Generate error bars object * * @param {array} errors - contains error bar values * @return error bar object */ function generateErrorBars(errors, visible){ let errorObject = { type: 'data', array: errors, visible: visible } return errorObject; } function generateErrorBarsAsymmetric(errorsPlus, errorsMinus, visible){ let errorObject = generateErrorBars(errorsPlus, visible); errorObject["arrayminus"] = errorsMinus; errorObject["symmetric"] = false; return errorObject; } function generateStackPlotData(){ let data = []; for (let index = 0; index < dataLabels.length; index++){ let inputType = dataProperties[dataLabels[index]]["chartType"]; let chartType = getPlotlyChartType(inputType); let trace = { x: xValues[index].slice(1), y: yValues[index].slice(1), stackgroup: 'one', name: dataLabels[index], type: chartType, marker:{ color: dataProperties[dataLabels[index]]["color"] } }; data.push(trace); } return data; } function generatePlotData(){ let data = []; for (let index = 0; index < dataLabels.length; index++){ let inputType = dataProperties[dataLabels[index]]["chartType"]; let chartType = getPlotlyChartType(inputType); let trace = { x: xValues[index].slice(1), y: yValues[index].slice(1), type: chartType, name: dataLabels[index], }; if(typeof xErrorValuesPlus[index] !== 'undefined'){ if(typeof xErrorValuesMinus[index] !== 'undefined' && xErrorValuesMinus[index].length > 0) { trace["error_x"] = generateErrorBarsAsymmetric(xErrorValuesPlus[index], xErrorValuesMinus[index], chartData.m_ShowErrorBars); }else{ trace["error_x"] = generateErrorBars(xErrorValuesPlus[index], chartData.m_ShowErrorBars); } } if(typeof yErrorValuesPlus[index] !== 'undefined'){ if(typeof yErrorValuesMinus[index] !== 'undefined' && yErrorValuesMinus[index].length > 0) { trace["error_y"] = generateErrorBarsAsymmetric(yErrorValuesPlus[index], yErrorValuesMinus[index], chartData.m_ShowErrorBars); }else{ trace["error_y"] = generateErrorBars(yErrorValuesPlus[index], chartData.m_ShowErrorBars); } } // ===================== CHART TYPE OPTIONS HANDLING =========== // initialize line object trace["line"] = {} trace["line"]["color"] = dataProperties[dataLabels[index]]["color"] if (chartType == "scatter"){ } else if (chartType == "area"){ trace["fill"] = 'tozeroy' } else if (chartType == "spline"){ trace["line"]["shape"] = 'spline' } else if (chartType == "scatterOnly"){ trace["mode"] = 'markers'; } else if (chartType == "area-spline"){ trace["fill"] = 'tozeroy' trace["line"]["shape"] = 'spline' } // handle marker visibility/size/color trace["marker"] = {size: chartData.m_DataPointSize, color: dataProperties[dataLabels[index]]["color"]} if (chartData.m_DataPointSize == 0){ trace["mode"] = "lines"; } if (dataProperties[dataLabels[index]]["style"] == "dashed"){ trace["line"]["dash"] = "dot" } data.push(trace) } return data; } /** * Here, the chart magic takes place. Plot.ly is called. * * @param {object} chartData - containing the options for plotting, not the actual values */ function generateChart(chartData) { console.log("generate chart"); if (chartData == undefined) { chartData = {} } if (dataLabels == undefined) { dataLabels = [] } //=============================== DATA ======================== var data = []; if (chartData.m_StackedData){ data = generateStackPlotData(); } else { data = generatePlotData(); } //=============================== STYLE ======================== let marginTop = chartData.m_chartTitle == undefined ? 10 : 50; if (chartData.m_LegendPosition == "bottomMiddle"){ var legendX = 0.5; var legendY = -0.75; } else if (chartData.m_LegendPosition == "bottomRight"){ var legendX = 1; var legendY = 0; } else if (chartData.m_LegendPosition == "topRight"){ var legendX = 1; var legendY = 1; } else if (chartData.m_LegendPosition == "topLeft"){ var legendX = 0; var legendY = 1; } else if (chartData.m_LegendPosition == "middleRight"){ var legendX = 1; var legendY = 0.5; } var layout = { paper_bgcolor : backgroundColor, plot_bgcolor : backgroundColor, title: { text:chartData.m_chartTitle, font: { color: foregroundColor } }, xaxis: { title: { text: chartData.m_xAxisLabel }, color: foregroundColor }, yaxis: { title: { text:chartData.m_yAxisLabel }, color: foregroundColor }, margin: { l: 50, r: 10, - b: 20, + b: 40, t: marginTop, pad: 4 }, showlegend: chartData.m_ShowLegend, legend: { x: legendX, y: legendY, font : { color: foregroundColor } } }; if (chartData.m_StackedData){ layout["barmode"] = 'stack'; } if (chartData.m_YAxisScale){ layout.yaxis["type"] = "log" } if (chartData.m_ShowSubchart){ layout.xaxis.rangeslider = {}; // adds range slider below x axis } Plotly.newPlot('chart', data, layout, {displayModeBar: false, responsive: true}); } /** * Change theme of chart. * * @param {string} color - dark or not dark */ function changeTheme(color) { setThemeColors(color); link = document.getElementsByTagName("link")[0]; if (color == 'dark') { link.href = "Chart_dark.css"; } else { link.href = "Chart.css"; } }; /** * Reload the chart with the given arguments. * * This method is called by C++. Changes on signature with caution. */ function Reload(){ console.log("Reload chart"); generateChart(chartData); } function SetShowSubchart(showSubchart) { chartData.m_ShowSubchart = showSubchart; } function setThemeColors(theme){ if (theme == 'dark'){ backgroundColor = '#2d2d30'; foregroundColor = 'white'; } else { backgroundColor = '#f0f0f0'; foregroundColor = 'black'; } } function SetStackDataString(stackDataString) { chartData.m_StackedData = stackDataString; } function SetShowErrorBars(showErrorBars) { chartData.m_ShowErrorBars = showErrorBars; } /** * Zooms to the given x-axis min and max values. */ function UpdateMinMaxValueXView(minValueX, maxValueX) { //y-Axis can't be adapted for now. See https://github.com/plotly/plotly.js/issues/1876 let chart = document.getElementById("chart"); let update = { xaxis:{ range:[minValueX, maxValueX] } }; Plotly.relayout(chart, update); } +/** + * Zooms to the given y-axis min and max values. + */ +function UpdateMinMaxValueYView(minValueY, maxValueY) +{ + //x-Axis can't be adapted for now. See https://github.com/plotly/plotly.js/issues/1876 + let chart = document.getElementById("chart"); + let update = { + yaxis:{ + range:[minValueY, maxValueY] + } + }; + Plotly.relayout(chart, update); +} + /** * Transforms the view to another chart type. * * This method is called by C++. Changes on signature with caution. * @param {string} transformTo - 'line' or 'bar' */ function transformView(transformTo) { console.log("transform view"); console.log(transformTo); dataProperties[dataLabels[0]]["chartType"] = transformTo; // preserve chartType for later updates let plotlyType = getPlotlyChartType(transformTo); let chart = document.getElementById("chart"); let update = {type : plotlyType}; Plotly.restyle(chart, update, 0); // updates the given plotly trace at index 0 with an update object built of a standard trace object }; diff --git a/Modules/Chart/src/QmitkChartWidget.cpp b/Modules/Chart/src/QmitkChartWidget.cpp index 69018d3f73..914eb7ce4a 100644 --- a/Modules/Chart/src/QmitkChartWidget.cpp +++ b/Modules/Chart/src/QmitkChartWidget.cpp @@ -1,660 +1,678 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include "mitkExceptionMacro.h" #include #include class CustomPage : public QWebEnginePage { public: CustomPage(QObject *parent = 0) : QWebEnginePage(parent) {} virtual void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel /*level*/, const QString &message, int lineNumber, const QString & /*sourceID*/) { MITK_INFO << "JS > " << lineNumber << ": " << message.toStdString(); } }; class QmitkChartWidget::Impl final { public: explicit Impl(QWidget *parent); ~Impl(); Impl(const Impl &) = delete; Impl &operator=(const Impl &) = delete; void AddData1D(const std::vector &data1D, const std::string &label, QmitkChartWidget::ChartType chartType); void AddData2D(const std::map &data2D, const std::string &label, QmitkChartWidget::ChartType chartType); void RemoveData(const std::string &label); void ClearData(); void SetColor(const std::string &label, const std::string &colorName); void SetLineStyle(const std::string &label, LineStyle style); void SetYAxisScale(AxisScale scale); void SetXAxisLabel(const std::string &label); void SetYAxisLabel(const std::string &label); void SetTitle(const std::string &title); void SetXErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus = std::vector()); void SetYErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus = std::vector()); std::string GetThemeName() const; void SetThemeName(ColorTheme style); void SetChartType(QmitkChartWidget::ChartType chartType); void SetLegendPosition(LegendPosition position); void SetChartTypeByLabel(const std::string &label, QmitkChartWidget::ChartType chartType); void Show(bool showSubChart); void SetShowLegend(bool show); void SetShowErrorBars(bool show); void SetStackedData(bool stacked); void SetShowDataPoints(bool showDataPoints = false); void SetChartType(const std::string &label, QmitkChartWidget::ChartType chartType); QList ConvertErrorVectorToQList(const std::vector &error); std::string ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const; void ClearJavaScriptChart(); void InitializeJavaScriptChart(); void CallJavaScriptFuntion(const QString &command); QSize sizeHint() const; private: using ChartxyDataVector = std::vector>; std::string GetUniqueLabelName(const QList &labelList, const std::string &label) const; QmitkChartxyData *GetDataElementByLabel(const std::string &label) const; QList GetDataLabels(const ChartxyDataVector &c3xyData) const; QWebChannel *m_WebChannel; QWebEngineView *m_WebEngineView; QmitkChartData m_C3Data; ChartxyDataVector m_C3xyData; std::map m_ChartTypeToName; std::map m_ColorThemeToName; std::map m_LegendPositionToName; std::map m_LineStyleToName; std::map m_AxisScaleToName; }; std::string QmitkChartWidget::Impl::GetThemeName() const { return m_C3Data.GetThemeName().toString().toStdString(); } QmitkChartWidget::Impl::Impl(QWidget *parent) : m_WebChannel(new QWebChannel(parent)), m_WebEngineView(new QWebEngineView(parent)) { // disable context menu for QWebEngineView m_WebEngineView->setContextMenuPolicy(Qt::NoContextMenu); m_WebEngineView->setPage(new CustomPage()); // Set the webengineview to an initial empty page. The actual chart will be loaded once the data is calculated. m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/empty.html"))); m_WebEngineView->page()->setWebChannel(m_WebChannel); m_WebEngineView->settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); connect(m_WebEngineView, SIGNAL(loadFinished(bool)), parent, SLOT(OnLoadFinished(bool))); auto layout = new QGridLayout(parent); layout->setMargin(0); layout->addWidget(m_WebEngineView); m_ChartTypeToName.emplace(ChartType::bar, "bar"); m_ChartTypeToName.emplace(ChartType::line, "line"); m_ChartTypeToName.emplace(ChartType::spline, "spline"); m_ChartTypeToName.emplace(ChartType::pie, "pie"); m_ChartTypeToName.emplace(ChartType::area, "area"); m_ChartTypeToName.emplace(ChartType::area_spline, "area-spline"); m_ChartTypeToName.emplace(ChartType::scatter, "scatter"); m_LegendPositionToName.emplace(LegendPosition::bottomMiddle, "bottomMiddle"); m_LegendPositionToName.emplace(LegendPosition::bottomRight, "bottomRight"); m_LegendPositionToName.emplace(LegendPosition::topRight, "topRight"); m_LegendPositionToName.emplace(LegendPosition::topLeft, "topLeft"); m_LegendPositionToName.emplace(LegendPosition::middleRight, "middleRight"); m_LineStyleToName.emplace(LineStyle::solid, "solid"); m_LineStyleToName.emplace(LineStyle::dashed, "dashed"); m_AxisScaleToName.emplace(AxisScale::linear, ""); m_AxisScaleToName.emplace(AxisScale::log, "log"); m_ColorThemeToName.emplace(ColorTheme::lightstyle, "light"); m_ColorThemeToName.emplace(ColorTheme::darkstyle, "dark"); } QmitkChartWidget::Impl::~Impl() {} std::string CheckForCorrectHex(const std::string &colorName) { std::regex rgx("([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})"); std::smatch match; if (!colorName.empty() && colorName.at(0) != '#' && std::regex_search(colorName.begin(), colorName.end(), match, rgx)) { return "#" + colorName; } else { return colorName; } } void QmitkChartWidget::Impl::AddData1D(const std::vector &data1D, const std::string &label, QmitkChartWidget::ChartType type) { std::map transformedData2D; unsigned int count = 0; // transform the 1D data to 2D data for (const auto &ele : data1D) { transformedData2D[count] = ele; count++; } AddData2D(transformedData2D, label, type); } void QmitkChartWidget::Impl::AddData2D(const std::map &data2D, const std::string &label, QmitkChartWidget::ChartType type) { QMap data2DConverted; for (const auto &aValue : data2D) { data2DConverted.insert(aValue.first, aValue.second); } const std::string chartTypeName(m_ChartTypeToName.at(type)); auto definedLabels = GetDataLabels(m_C3xyData); auto uniqueLabel = GetUniqueLabelName(definedLabels, label); if (type == ChartType::scatter) { SetShowDataPoints(true); MITK_INFO << "Enabling data points for all because of scatter plot"; } m_C3xyData.push_back(std::make_unique( data2DConverted, QVariant(QString::fromStdString(uniqueLabel)), QVariant(QString::fromStdString(chartTypeName)))); } void QmitkChartWidget::Impl::RemoveData(const std::string &label) { for (ChartxyDataVector::iterator iter = m_C3xyData.begin(); iter != m_C3xyData.end(); ++iter) { if ((*iter)->GetLabel().toString().toStdString() == label) { m_C3xyData.erase(iter); return; } } throw std::invalid_argument("Cannot Remove Data because the label does not exist."); } void QmitkChartWidget::Impl::ClearData() { for (auto &xyData : m_C3xyData) { m_WebChannel->deregisterObject(xyData.get()); } m_C3xyData.clear(); } void QmitkChartWidget::Impl::SetColor(const std::string &label, const std::string &colorName) { auto element = GetDataElementByLabel(label); if (element) { auto colorChecked = CheckForCorrectHex(colorName); element->SetColor(QVariant(QString::fromStdString(colorChecked))); } } void QmitkChartWidget::Impl::SetLineStyle(const std::string &label, LineStyle style) { auto element = GetDataElementByLabel(label); const std::string lineStyleName(m_LineStyleToName.at(style)); element->SetLineStyle(QVariant(QString::fromStdString(lineStyleName))); } void QmitkChartWidget::Impl::SetYAxisScale(AxisScale scale) { const std::string axisScaleName(m_AxisScaleToName.at(scale)); m_C3Data.SetYAxisScale(QString::fromStdString(axisScaleName)); } QmitkChartxyData *QmitkChartWidget::Impl::GetDataElementByLabel(const std::string &label) const { for (const auto &qmitkChartxyData : m_C3xyData) { if (qmitkChartxyData->GetLabel().toString() == label.c_str()) { return qmitkChartxyData.get(); } } MITK_WARN << "label " << label << " not found in QmitkChartWidget"; return nullptr; } QList QmitkChartWidget::Impl::GetDataLabels(const ChartxyDataVector &c3xyData) const { QList dataLabels; for (auto element = c3xyData.begin(); element != c3xyData.end(); ++element) { dataLabels.push_back((*element)->GetLabel()); } return dataLabels; } void QmitkChartWidget::Impl::SetXAxisLabel(const std::string &label) { m_C3Data.SetXAxisLabel(QString::fromStdString(label)); } void QmitkChartWidget::Impl::SetYAxisLabel(const std::string &label) { m_C3Data.SetYAxisLabel(QString::fromStdString(label)); } void QmitkChartWidget::Impl::SetTitle(const std::string &title) { m_C3Data.SetTitle(QString::fromStdString(title)); } void QmitkChartWidget::Impl::SetThemeName(QmitkChartWidget::ColorTheme style) { const std::string themeName(m_ColorThemeToName.at(style)); m_C3Data.SetThemeName(QString::fromStdString(themeName)); } void QmitkChartWidget::Impl::SetChartType(QmitkChartWidget::ChartType chartType) { for (auto iterator = m_C3xyData.begin(); iterator != m_C3xyData.end(); ++iterator) { SetChartTypeByLabel((*iterator)->GetLabel().toString().toStdString(), chartType); } auto chartTypeName = ConvertChartTypeToString(chartType); const QString command = QString::fromStdString("transformView('" + chartTypeName + "')"); CallJavaScriptFuntion(command); } void QmitkChartWidget::Impl::SetChartTypeByLabel(const std::string &label, QmitkChartWidget::ChartType chartType) { auto element = GetDataElementByLabel(label); if (element) { if (chartType == ChartType::scatter) { SetShowDataPoints(true); MITK_INFO << "Enabling data points for all because of scatter plot"; } auto chartTypeName = ConvertChartTypeToString(chartType); element->SetChartType(QVariant(QString::fromStdString(chartTypeName))); } } void QmitkChartWidget::Impl::SetLegendPosition(QmitkChartWidget::LegendPosition legendPosition) { const std::string legendPositionName(m_LegendPositionToName.at(legendPosition)); m_C3Data.SetLegendPosition(QString::fromStdString(legendPositionName)); } void QmitkChartWidget::Impl::Show(bool showSubChart) { if (m_C3xyData.empty()) { MITK_WARN << "no data available for display in chart"; } else { m_C3Data.SetAppearance(showSubChart, m_C3xyData.front()->GetChartType() == QVariant("pie")); } InitializeJavaScriptChart(); } void QmitkChartWidget::Impl::SetShowLegend(bool show) { m_C3Data.SetShowLegend(show); } void QmitkChartWidget::Impl::SetStackedData(bool stacked) { m_C3Data.SetStackedData(stacked); } void QmitkChartWidget::Impl::SetShowErrorBars(bool show) { m_C3Data.SetShowErrorBars(show); } void QmitkChartWidget::Impl::SetShowDataPoints(bool showDataPoints) { if (showDataPoints == true) { m_C3Data.SetDataPointSize(6.5); } else { m_C3Data.SetDataPointSize(0); } } void QmitkChartWidget::Impl::SetChartType(const std::string &label, QmitkChartWidget::ChartType chartType) { auto element = GetDataElementByLabel(label); if (element) { if (chartType == ChartType::scatter) { SetShowDataPoints(true); MITK_INFO << "Enabling data points for all because of scatter plot"; } const std::string chartTypeName(m_ChartTypeToName.at(chartType)); element->SetChartType(QVariant(QString::fromStdString(chartTypeName))); } } QList QmitkChartWidget::Impl::ConvertErrorVectorToQList(const std::vector &error) { QList errorConverted; for (const auto &aValue : error) { errorConverted.append(aValue); } return errorConverted; } void QmitkChartWidget::Impl::SetXErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus) { auto element = GetDataElementByLabel(label); if (element) { auto errorConvertedPlus = ConvertErrorVectorToQList(errorPlus); auto errorConvertedMinus = ConvertErrorVectorToQList(errorMinus); element->SetXErrorDataPlus(errorConvertedPlus); element->SetXErrorDataMinus(errorConvertedMinus); } } void QmitkChartWidget::Impl::SetYErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus) { auto element = GetDataElementByLabel(label); if (element) { auto errorConvertedPlus = ConvertErrorVectorToQList(errorPlus); auto errorConvertedMinus = ConvertErrorVectorToQList(errorMinus); element->SetYErrorDataPlus(errorConvertedPlus); element->SetYErrorDataMinus(errorConvertedMinus); } } std::string QmitkChartWidget::Impl::ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const { return m_ChartTypeToName.at(chartType); } QSize QmitkChartWidget::Impl::sizeHint() const { return QSize(400, 300); } void QmitkChartWidget::Impl::CallJavaScriptFuntion(const QString &command) { m_WebEngineView->page()->runJavaScript(command); } void QmitkChartWidget::Impl::ClearJavaScriptChart() { m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/empty.html"))); } void QmitkChartWidget::Impl::InitializeJavaScriptChart() { - m_WebChannel->registerObject(QStringLiteral("chartData"), &m_C3Data); + auto alreadyRegisteredObjects = m_WebChannel->registeredObjects(); + auto alreadyRegisteredObjectsValues = alreadyRegisteredObjects.values(); + //only register objects that have not been registered yet + if (alreadyRegisteredObjectsValues.indexOf(&m_C3Data) == -1) + { + m_WebChannel->registerObject(QStringLiteral("chartData"), &m_C3Data); + } + unsigned count = 0; for (auto &xyData : m_C3xyData) { - QString variableName = "xyData" + QString::number(count); - m_WebChannel->registerObject(variableName, xyData.get()); + // only register objects that have not been registered yet + if (alreadyRegisteredObjectsValues.indexOf(xyData.get()) == -1) + { + QString variableName = "xyData" + QString::number(count); + m_WebChannel->registerObject(variableName, xyData.get()); + } count++; } m_WebEngineView->load(QUrl(QStringLiteral("qrc:///Chart/QmitkChartWidget.html"))); } std::string QmitkChartWidget::Impl::GetUniqueLabelName(const QList &labelList, const std::string &label) const { QString currentLabel = QString::fromStdString(label); int counter = 0; while (labelList.contains(currentLabel)) { currentLabel = QString::fromStdString(label + std::to_string(counter)); counter++; } return currentLabel.toStdString(); } QmitkChartWidget::QmitkChartWidget(QWidget *parent) : QWidget(parent), m_Impl(new Impl(this)) { connect(this, &QmitkChartWidget::PageSuccessfullyLoaded, this, &QmitkChartWidget::OnPageSuccessfullyLoaded); } QmitkChartWidget::~QmitkChartWidget() {} void QmitkChartWidget::AddData2D(const std::map &data2D, const std::string &label, ChartType type) { m_Impl->AddData2D(data2D, label, type); } void QmitkChartWidget::SetColor(const std::string &label, const std::string &colorName) { m_Impl->SetColor(label, colorName); } void QmitkChartWidget::SetLineStyle(const std::string &label, LineStyle style) { m_Impl->SetLineStyle(label, style); } void QmitkChartWidget::SetYAxisScale(AxisScale scale) { m_Impl->SetYAxisScale(scale); } void QmitkChartWidget::AddData1D(const std::vector &data1D, const std::string &label, ChartType type) { m_Impl->AddData1D(data1D, label, type); } void QmitkChartWidget::RemoveData(const std::string &label) { m_Impl->RemoveData(label); } void QmitkChartWidget::SetXAxisLabel(const std::string &label) { m_Impl->SetXAxisLabel(label); } void QmitkChartWidget::SetYAxisLabel(const std::string &label) { m_Impl->SetYAxisLabel(label); } void QmitkChartWidget::SetTitle(const std::string &title) { m_Impl->SetTitle(title); } void QmitkChartWidget::SetShowDataPoints(bool showDataPoints) { m_Impl->SetShowDataPoints(showDataPoints); } void QmitkChartWidget::SetChartType(const std::string &label, ChartType type) { m_Impl->SetChartType(label, type); } void QmitkChartWidget::SetXErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus) { m_Impl->SetXErrorBars(label, errorPlus, errorMinus); } void QmitkChartWidget::SetYErrorBars(const std::string &label, const std::vector &errorPlus, const std::vector &errorMinus) { m_Impl->SetYErrorBars(label, errorPlus, errorMinus); } void QmitkChartWidget::SetLegendPosition(LegendPosition position) { m_Impl->SetLegendPosition(position); } void QmitkChartWidget::SetShowLegend(bool show) { m_Impl->SetShowLegend(show); } void QmitkChartWidget::SetStackedData(bool stacked) { m_Impl->SetStackedData(stacked); } void QmitkChartWidget::Show(bool showSubChart) { m_Impl->Show(showSubChart); } void QmitkChartWidget::Clear() { m_Impl->ClearData(); m_Impl->ClearJavaScriptChart(); } void QmitkChartWidget::OnLoadFinished(bool isLoadSuccessful) { if (isLoadSuccessful) { emit PageSuccessfullyLoaded(); } } void QmitkChartWidget::OnPageSuccessfullyLoaded() { auto themeName = m_Impl->GetThemeName(); QString command; if (themeName == "dark") { command = QString("changeTheme('dark')"); } else { command = QString("changeTheme('light')"); } m_Impl->CallJavaScriptFuntion(command); } std::string QmitkChartWidget::convertBooleanValue(bool value) const { std::stringstream converter; converter << std::boolalpha << value; return converter.str(); } void QmitkChartWidget::SetChartTypeForAllDataAndReload(ChartType type) { m_Impl->SetChartType(type); } void QmitkChartWidget::SetTheme(ColorTheme themeEnabled) { m_Impl->SetThemeName(themeEnabled); } void QmitkChartWidget::SetShowSubchart(bool showSubChart) { QString subChartString = QString::fromStdString(convertBooleanValue(showSubChart)); const QString command = QString("SetShowSubchart(" + subChartString + ")"); m_Impl->CallJavaScriptFuntion(command); } void QmitkChartWidget::SetShowErrorBars(bool showErrorBars) { m_Impl->SetShowErrorBars(showErrorBars); } void QmitkChartWidget::UpdateMinMaxValueXView(double minValueX, double maxValueX) { QString minMaxValueXString = QString::fromStdString(std::to_string(minValueX))+QString(","); minMaxValueXString += QString::fromStdString(std::to_string(maxValueX)); const QString command = QString("UpdateMinMaxValueXView(" + minMaxValueXString + ")"); m_Impl->CallJavaScriptFuntion(command); } +void QmitkChartWidget::UpdateMinMaxValueYView(double minValueY, double maxValueY) { + QString minMaxValueYString = QString::fromStdString(std::to_string(minValueY)) + QString(","); + minMaxValueYString += QString::fromStdString(std::to_string(maxValueY)); + const QString command = QString("UpdateMinMaxValueYView(" + minMaxValueYString + ")"); + m_Impl->CallJavaScriptFuntion(command); +} + void QmitkChartWidget::Reload() { const QString command = QString("Reload()"); m_Impl->CallJavaScriptFuntion(command); } QSize QmitkChartWidget::sizeHint() const { return m_Impl->sizeHint(); } diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index 7b481c1ce2..b4f33cdb1e 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,320 +1,321 @@ 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 Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAnatomicalStructureColorPresets.cpp DataManagement/mitkArbitraryTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkGenericIDRelationRule.cpp DataManagement/mitkIdentifiable.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyOwner.cpp DataManagement/mitkIPropertyPersistence.cpp DataManagement/mitkIPropertyProvider.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDataUID.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateFunction.cpp DataManagement/mitkNodePredicateGeometry.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateDataProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyKeyPath.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyNameHelper.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkPropertyPersistence.cpp DataManagement/mitkPropertyPersistenceInfo.cpp DataManagement/mitkPropertyRelationRuleBase.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkScaleOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp + DataManagement/mitkSourceImageRelationRule.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkUIDManipulator.cpp DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkIPropertyRelations.cpp DataManagement/mitkPropertyRelations.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkDisplayActionEventBroadcast.cpp Interactions/mitkDisplayActionEventFunctions.cpp Interactions/mitkDisplayActionEventHandler.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionSchemeSwitcher.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkStdDisplayActionEventHandler.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp IO/mitkLocaleSwitch.cpp IO/mitkLog.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkProportionalTimeGeometryToXML.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkPreferenceListReaderOptionsFunctor.cpp Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkMapper.cpp Rendering/mitkAnnotation.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp #Rendering/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/DisplayConfigMITKNoCrosshair.xml Interactions/DisplayConfigMITKRotation.xml Interactions/DisplayConfigMITKRotationUnCoupled.xml Interactions/DisplayConfigMITKSwivel.xml Interactions/DisplayConfigMITKLimited.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml mitkAnatomicalStructureColorPresets.xml ) diff --git a/Modules/Core/include/mitkAnnotationUtils.h b/Modules/Core/include/mitkAnnotationUtils.h index ee4a42f2cf..6502fa70b8 100644 --- a/Modules/Core/include/mitkAnnotationUtils.h +++ b/Modules/Core/include/mitkAnnotationUtils.h @@ -1,95 +1,96 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkAnnotationUtils_h #define mitkAnnotationUtils_h #include #include +#include #include class vtkObject; namespace mitk { class AbstractAnnotationRenderer; class Annotation; class BaseRenderer; /** * @brief The AnnotationUtils class provides static functions for accsessing registered AnnotationRenderers and * Annotations */ class MITKCORE_EXPORT AnnotationUtils { public: typedef std::vector> AnnotationRendererServices; AnnotationUtils(); ~AnnotationUtils(); /** * @brief GetAnnotationRenderer returns a registered AnnotationRenderer of a specific type and for a BaseRenderer * @param arTypeID name specifier of the AnnotationRenderer * @param rendererID name specifier of the BaseRenderer * @return */ static AbstractAnnotationRenderer *GetAnnotationRenderer(const std::string &arTypeID, const std::string &rendererID); /** * @brief RegisterAnnotationRenderer registers an AnnotationRenderer as a microservice and saves a reference to it * in a local static list. * @param annotationRenderer */ static void RegisterAnnotationRenderer(AbstractAnnotationRenderer *annotationRenderer); /** * @brief GetAnnotationRenderer returns a list of registered AnnotationRenderers for a specified BaseRenderer * @param rendererID name specifier of the BaseRenderer * @return */ static std::vector GetAnnotationRenderer(const std::string &rendererID); /** * @brief UpdateAnnotationRenderer is a convenience function which calls AbstractAnnotationRenderer::Update for each * registered AnnotationRenderer of a specific BaseRenderer. * @param rendererID */ static void UpdateAnnotationRenderer(const std::string &rendererID); /** * @brief BaseRendererChanged has to be called in the case that the actual BaseRenderer object for a BaseRenderer ID * has changed. E.g. if a RenderWindow was closed and reopened. * @param renderer The new BaseRenderer */ static void BaseRendererChanged(BaseRenderer *renderer); /** * @brief GetAnnotation returns a registered Annotation for a specified ID. * @param AnnotationID * @return */ static mitk::Annotation *GetAnnotation(const std::string &AnnotationID); private: AnnotationUtils(const AnnotationUtils &); AnnotationUtils &operator=(const AnnotationUtils &); static void RenderWindowCallback(vtkObject *caller, unsigned long, void *, void *); }; } #endif diff --git a/Modules/Core/include/mitkLevelWindowManager.h b/Modules/Core/include/mitkLevelWindowManager.h index 8101380fdf..3e8826d6b3 100644 --- a/Modules/Core/include/mitkLevelWindowManager.h +++ b/Modules/Core/include/mitkLevelWindowManager.h @@ -1,161 +1,219 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#ifndef mitkLevelWindowManager_h -#define mitkLevelWindowManager_h +#ifndef MITKLEVELWINDOWMANAGER_H +#define MITKLEVELWINDOWMANAGER_H +// mitk core #include "mitkBaseProperty.h" #include "mitkDataStorage.h" #include "mitkLevelWindowProperty.h" + +// c++ #include #include namespace mitk { /** - \brief Provides access to the LevelWindowProperty object and LevelWindow of the "current" image. + @brief Provides access to the LevelWindowProperty object and LevelWindow of the "current" image. - provides a LevelWindowProperty for purposes like GUI editors - this property comes from one of two possible sources - either something (e.g. the application) sets the property because of some user selection - OR the "Auto top-most" logic is used to search a DataStorage for the image with the highest "layer" property - value + value Changes on Level/Window can be set with SetLevelWindow() and will affect either the topmost layer image, if isAutoTopMost() returns true, or an image which is set by SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty). Changes to Level/Window, when another image gets active or by SetLevelWindow(const LevelWindow& levelWindow), will be sent to all listeners by Modified(). DataStorageChanged() listens to the DataStorage for new or removed images. Depending on how m_AutoTopMost is set, the new image becomes active or not. If an image is removed from the DataStorage and m_AutoTopMost is false, there is a check to proof, if the active image is still available. If not, then m_AutoTopMost becomes true. Note that this class is not thread safe at the moment! */ - class MITKCORE_EXPORT LevelWindowManager : public itk::Object { public: - mitkClassMacroItkParent(LevelWindowManager, itk::Object) itkFactorylessNewMacro(Self) itkCloneMacro(Self) - - void SetDataStorage(DataStorage *ds); - DataStorage *GetDataStorage(); ///< returns the datastorage - /** @brief (Re-)Initializes the LevelWindowManager by setting the topmost image. - * Use the removedNode parameter if a node was removed... - * @param autoTopMost true: sets the topmost layer image to be affected by changes - * @param removedNode != nullptr a node was removed from DataStorage */ - void SetAutoTopMostImage(bool autoTopMost, const DataNode *removedNode = nullptr); + mitkClassMacroItkParent(LevelWindowManager, itk::Object) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) - void RecaluclateLevelWindowForSelectedComponent(const itk::EventObject &); + void SetDataStorage(DataStorage *ds); + DataStorage *GetDataStorage(); - void Update(const itk::EventObject &e); ///< gets called if a visible property changes + /** + * @brief (Re-)Initialize the LevelWindowManager by setting the topmost image. + * Use the removedNode parameter if a node was removed. + * + * @param autoTopMost Set the topmost layer image to be affected by changes, if true. + * @param removedNode A node was removed from the data storage if != nullptr. + */ + void SetAutoTopMostImage(bool autoTopMost, const DataNode *removedNode = nullptr); + /** + * @brief (Re-)Initialize the LevelWindowManager by setting the selected images. + * Use the removedNode parameter if a node was removed. + * + * @param selectedImagesMode Set the selected images to be affected by changes, if true. + * @param removedNode A node was removed from the data storage if != nullptr. + */ + void SetSelectedImages(bool selectedImagesMode, const DataNode *removedNode = nullptr); - /** @brief Sets an specific LevelWindowProperty, all changes will affect the image belonging to this property. - * @throw mitk::Exception Throws an exception if the there is no image in the data storage which belongs to this - * property.*/ + void RecalculateLevelWindowForSelectedComponent(const itk::EventObject&); + /** + * @brief Update the level window. + * This function is called if a property of a data node is changed. + * Relevant properties are defined in the protected 'ObserverToPropertyValueMap'-members. + */ + void Update(const itk::EventObject&); + /** + * @brief Update the level window. + * This function is only called if the 'selected' property of a data node is changed. + * This is done in order to avoid finding the correct image each time a node is selected but + * the 'm_SelectedImages' bool value is set to false (as the normal 'Update'-function would do). + * Changes of the 'selected' property happen quite a lot so this should not slow down the application. + */ + void UpdateSelected(const itk::EventObject&); + /** + * @brief Set a specific LevelWindowProperty; all changes will affect the image belonging to this property. + * @throw mitk::Exception Throw an exception if the there is no image in the data storage which belongs to this + * property. + */ void SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty); - - /** @brief Sets new Level/Window values and informs all listeners about changes. */ + /** + * @brief Set new Level/Window values and inform all listeners about changes. + */ void SetLevelWindow(const LevelWindow &levelWindow); - - /** @return Returns Level/Window values for the current image.*/ - const LevelWindow &GetLevelWindow(); - - /** @return Returns the current mitkLevelWindowProperty object from the image that is affected by changes.*/ + /** + * @brief Return the current LevelWindowProperty object from the image that is affected by changes. + * + * @return The current LevelWindowProperty + */ LevelWindowProperty::Pointer GetLevelWindowProperty(); - - /** @return true if changes on slider or line-edits will affect always the topmost layer image. */ - bool isAutoTopMost(); - + /** + * @brief Return Level/Window values for the current image + * + * @return The LevelWindow value for the current image. + */ + const LevelWindow &GetLevelWindow(); + /** + * @brief Return true, if the changes on slider or line-edits will affect the topmost layer image. + * + * @return Return the member value that denotes the auto-topmost mode. + */ + bool IsAutoTopMost(); + /** + * @brief Return true, if changes on slider or line-edits will affect the currently selected images. + * + * @return Return the member value that denotes the selected-images mode. + */ + bool IsSelectedImages(); /** @brief This method is called when a node is added to the data storage. - * A listener on the data storage is used to call this method automatically after a node was added. - * @throw mitk::Exception Throws an exception if something is wrong, e.g. if the number of observers differs from - * the - * number of nodes. - */ + * A listener on the data storage is used to call this method automatically after a node was added. + * @throw mitk::Exception Throws an exception if something is wrong, e.g. if the number of observers differs from + * the number of nodes. + */ void DataStorageAddedNode(const DataNode *n = nullptr); - /** @brief This method is called when a node is removed to the data storage. - * A listener on the data storage is used to call this method automatically directly before a node will be - * removed. - * @throw mitk::Exception Throws an exception if something is wrong, e.g. if the number of observers differs from - * the - * number of nodes. - */ + * A listener on the data storage is used to call this method automatically directly before a node will be + * removed. + * @throw mitk::Exception Throws an exception if something is wrong, e.g. if the number of observers differs from + * the number of nodes. + */ void DataStorageRemovedNode(const DataNode *removedNode = nullptr); - - /** @brief change notifications from mitkLevelWindowProperty */ + /** + * @brief Change notifications from mitkLevelWindowProperty. + */ void OnPropertyModified(const itk::EventObject &e); - - Image *GetCurrentImage(); ///< return the currently active image - + /** + * @brief Return the currently active image. + * + * @return The member variable holding the currently active image. + */ + Image *GetCurrentImage(); /** * @return Returns the current number of observers which are registered in this object. * @throw mitk::Exception Throws an exception if the number of observers differs from * the number of relevant objects * which means that something is wrong. * */ int GetNumberOfObservers(); /** - * returns all nodes in the DataStorage that have the following properties: - * "visible" == true, "binary" == false, "levelwindow", and DataType == Image + * @brief Returns all nodes in the DataStorage that have the following properties: + * - "binary" == false + * - "levelwindow" + * - DataType == Image / DiffusionImage / TensorImage / OdfImage / ShImage */ DataStorage::SetOfObjects::ConstPointer GetRelevantNodes(); protected: LevelWindowManager(); ~LevelWindowManager() override; DataStorage::Pointer m_DataStorage; - LevelWindowProperty::Pointer m_LevelWindowProperty; ///< pointer to the LevelWindowProperty of the current image + /// Pointer to the LevelWindowProperty of the current image. + LevelWindowProperty::Pointer m_LevelWindowProperty; + typedef std::pair PropDataPair; - typedef std::map ObserverToPropertyMap; - - ObserverToPropertyMap - m_PropObserverToNode; ///< map to hold observer IDs to every visible property of DataNode�s BaseProperty - ObserverToPropertyMap - m_PropObserverToNode2; ///< map to hold observer IDs to every layer property of DataNode�s BaseProperty - ObserverToPropertyMap m_PropObserverToNode3; ///< map to hold observer IDs to every Image Rendering.Mode property of - /// DataNode�s BaseProperty - ObserverToPropertyMap - m_PropObserverToNode4; ///< map to hold observer IDs to every Image.Displayed Component property - /// of DataNode�s BaseProperty - ObserverToPropertyMap - m_PropObserverToNode5; ///< map to hold observer IDs to every image for level window property of - /// DataNode�s BaseProperty - - void UpdateObservers(); ///< updates the internal observer list. Ignores nodes which are marked to be deleted in the - /// variable m_NodeMarkedToDelete - void ClearPropObserverLists(); ///< internal help method to clear both lists/maps. - void CreatePropObserverLists(); ///< internal help method to create both lists/maps. - const mitk::DataNode *m_NodeMarkedToDelete; ///< this variable holds a data node which will be deleted from the - /// datastorage immedeately (if there is one, nullptr otherways) + typedef std::map ObserverToPropertyValueMap; + /// Map to hold observer IDs to every "visible" property of DataNode's BaseProperty. + ObserverToPropertyValueMap m_ObserverToVisibleProperty; + /// Map to hold observer IDs to every "layer" property of DataNode's BaseProperty. + ObserverToPropertyValueMap m_ObserverToLayerProperty; + /// Map to hold observer IDs to every "Image Rendering.Mode" property of DataNode's BaseProperty. + ObserverToPropertyValueMap m_ObserverToRenderingModeProperty; + /// Map to hold observer IDs to every "Image.Displayed Component" property of DataNode's BaseProperty. + ObserverToPropertyValueMap m_ObserverToDisplayedComponentProperty; + /// Map to hold observer IDs to every "imageForLevelWindow" property of DataNode's BaseProperty. + ObserverToPropertyValueMap m_ObserverToLevelWindowImageProperty; + /// Map to hold observer IDs to every "selected" property of DataNode's BaseProperty. + ObserverToPropertyValueMap m_ObserverToSelectedProperty; + + /// Updates the internal observer list. + /// Ignores nodes which are marked to be deleted in the variable m_NodeMarkedToDelete. + void UpdateObservers(); + /// Internal help method to clear both lists/maps. + void ClearPropObserverLists(); + /// Internal help method to create both lists/maps. + void CreatePropObserverLists(); + + bool IgnoreNode(const DataNode* dataNode); + + /// This variable holds a data node which will be deleted from the datastorage immediately + /// Nullptr, if there is no data node to be deleted. + const DataNode *m_NodeMarkedToDelete; bool m_AutoTopMost; + bool m_SelectedImagesMode; unsigned long m_ObserverTag; bool m_IsObserverTagSet; unsigned long m_PropertyModifiedTag; Image *m_CurrentImage; + std::vector m_RelevantDataNodes; bool m_IsPropertyModifiedTagSet; - bool m_SettingImgForLvlWinProp; + bool m_LevelWindowMutex; }; } -#endif + +#endif // MITKLEVELWINDOWMANAGER_H diff --git a/Modules/Core/include/mitkPlaneGeometry.h b/Modules/Core/include/mitkPlaneGeometry.h index 4e296c981b..c287d67541 100644 --- a/Modules/Core/include/mitkPlaneGeometry.h +++ b/Modules/Core/include/mitkPlaneGeometry.h @@ -1,608 +1,611 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /** * \brief Describes the geometry of a plane object * * Describes a two-dimensional manifold, i.e., to put it simply, * an object that can be described using a 2D coordinate-system. * * PlaneGeometry can map points between 3D world coordinates * (in mm) and the described 2D coordinate-system (in mm) by first projecting * the 3D point onto the 2D manifold and then calculating the 2D-coordinates * (in mm). These 2D-mm-coordinates can be further converted into * 2D-unit-coordinates (e.g., pixels), giving a parameter representation of * the object with parameter values inside a rectangle * (e.g., [0,0]..[width, height]), which is the bounding box (bounding range * in z-direction always [0]..[1]). * * A PlaneGeometry describes the 2D representation within a 3D object (derived from BaseGeometry). For example, * a single CT-image (slice) is 2D in the sense that you can access the * pixels using 2D-coordinates, but is also 3D, as the pixels are really * voxels, thus have an extension (thickness) in the 3rd dimension. * * * Optionally, a reference BaseGeometry can be specified, which usually would * be the geometry associated with the underlying dataset. This is currently * used for calculating the intersection of inclined / rotated planes * (represented as PlaneGeometry) with the bounding box of the associated * BaseGeometry. * * \warning The PlaneGeometry are not necessarily up-to-date and not even * initialized. As described in the previous paragraph, one of the * Generate-/Copy-/UpdateOutputInformation methods have to initialize it. * mitk::BaseData::GetPlaneGeometry() makes sure, that the PlaneGeometry is * up-to-date before returning it (by setting the update extent appropriately * and calling UpdateOutputInformation). * * Rule: everything is in mm (or ms for temporal information) if not * stated otherwise. * \ingroup Geometry */ #ifndef PLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #define PLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C #include "mitkBaseGeometry.h" #include "mitkRestorePlanePositionOperation.h" #include #include namespace mitk { template class Line; typedef Line Line3D; class PlaneGeometry; /** \deprecatedSince{2014_10} This class is deprecated. Please use PlaneGeometry instead. */ DEPRECATED(typedef PlaneGeometry Geometry2D); /** * \brief Describes a two-dimensional, rectangular plane * * \ingroup Geometry */ class MITKCORE_EXPORT PlaneGeometry : public BaseGeometry { public: mitkClassMacro(PlaneGeometry, BaseGeometry); /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) enum PlaneOrientation { Axial, Sagittal, Frontal, // also known as "Coronal" in mitk. None // This defines the PlaneGeometry for the 3D renderWindow which // curiously also needs a PlaneGeometry. This should be reconsidered some time. }; virtual void IndexToWorld(const Point2D &pt_units, Point2D &pt_mm) const; virtual void WorldToIndex(const Point2D &pt_mm, Point2D &pt_units) const; //##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 virtual void IndexToWorld(const mitk::Point2D &atPt2d_untis, const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const; //##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 virtual void IndexToWorld(const mitk::Vector2D &vec_units, mitk::Vector2D &vec_mm) const; //##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 virtual void WorldToIndex(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_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 virtual void WorldToIndex(const mitk::Vector2D &vec_mm, mitk::Vector2D &vec_units) const; /** * \brief Initialize a plane with orientation \a planeorientation * (default: axial) with respect to \a BaseGeometry (default: identity). * Spacing also taken from \a BaseGeometry. * * \warning A former version of this method created a geometry with unit * spacing. For unit spacing use * * \code * // for in-plane unit spacing: * thisgeometry->SetSizeInUnits(thisgeometry->GetExtentInMM(0), * thisgeometry->GetExtentInMM(1)); * // additionally, for unit spacing in normal direction (former version * // did not do this): * thisgeometry->SetExtentInMM(2, 1.0); * \endcode */ virtual void InitializeStandardPlane(const BaseGeometry *geometry3D, PlaneOrientation planeorientation = Axial, ScalarType zPosition = 0, bool frontside = true, bool rotated = false, bool top = true); /** * \brief Initialize a plane with orientation \a planeorientation * (default: axial) with respect to \a BaseGeometry (default: identity). * Spacing also taken from \a BaseGeometry. * * \param top if \a true, create plane at top, otherwise at bottom * (for PlaneOrientation Axial, for other plane locations respectively) */ virtual void InitializeStandardPlane(const BaseGeometry *geometry3D, bool top, PlaneOrientation planeorientation = Axial, bool frontside = true, bool rotated = false); /** * \brief Initialize a plane with orientation \a planeorientation * (default: axial) with respect to \a transform (default: identity) * given width and height in units. * * \a Rotated means rotated by 180 degrees (1/2 rotation) within the plane. * Rotation by 90 degrees (1/4 rotation) is not implemented as of now. * * \a Frontside/Backside: * Viewed from below = frontside in the axial case; * (radiologist's view versus neuro-surgeon's view, see: * http://www.itk.org/Wiki/images/e/ed/DICOM-OrientationDiagram-Radiologist-vs-NeuroSurgeon.png ) * Viewed from front = frontside in the coronal case; * Viewed from left = frontside in the sagittal case. * * \a Cave/Caution: Currently only RPI, LAI, LPS and RAS in the three standard planes are covered, * i.e. 12 cases of 144: 3 standard planes * 48 coordinate orientations = 144 cases. */ virtual void InitializeStandardPlane(ScalarType width, ScalarType height, const AffineTransform3D *transform = nullptr, PlaneOrientation planeorientation = Axial, ScalarType zPosition = 0, bool frontside = true, bool rotated = false, bool top = true); /** * \brief Initialize plane with orientation \a planeorientation * (default: axial) given width, height and spacing. * */ virtual void InitializeStandardPlane(ScalarType width, ScalarType height, const Vector3D &spacing, PlaneOrientation planeorientation = Axial, ScalarType zPosition = 0, bool frontside = true, bool rotated = false, bool top = true); /** * \brief Initialize plane by width and height in pixels, right-/down-vector * (itk) to describe orientation in world-space (vectors will be normalized) * and spacing (default: 1.0 mm in all directions). * * The vectors are normalized and multiplied by the respective spacing before * they are set in the matrix. * * This overloaded version of InitializeStandardPlane() creates only righthanded * coordinate orientations, unless spacing contains 1 or 3 negative entries. * */ virtual void InitializeStandardPlane(ScalarType width, ScalarType height, const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing = nullptr); /** * \brief Initialize plane by width and height in pixels, * right-/down-vector (vnl) to describe orientation in world-space (vectors * will be normalized) and spacing (default: 1.0 mm in all directions). * * The vectors are normalized and multiplied by the respective spacing * before they are set in the matrix. * * This overloaded version of InitializeStandardPlane() creates only righthanded * coordinate orientations, unless spacing contains 1 or 3 negative entries. * */ virtual void InitializeStandardPlane(ScalarType width, ScalarType height, const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing = nullptr); /** * \brief Initialize plane by right-/down-vector (itk) and spacing * (default: 1.0 mm in all directions). * * The length of the right-/-down-vector is used as width/height in units, * respectively. Then, the vectors are normalized and multiplied by the * respective spacing before they are set in the matrix. */ virtual void InitializeStandardPlane(const Vector3D &rightVector, const Vector3D &downVector, const Vector3D *spacing = nullptr); /** * \brief Initialize plane by right-/down-vector (vnl) and spacing * (default: 1.0 mm in all directions). * * The length of the right-/-down-vector is used as width/height in units, * respectively. Then, the vectors are normalized and multiplied by the * respective spacing before they are set in the matrix. */ virtual void InitializeStandardPlane(const VnlVector &rightVector, const VnlVector &downVector, const Vector3D *spacing = nullptr); /** * \brief Initialize plane by origin and normal (size is 1.0 mm in * all directions, direction of right-/down-vector valid but * undefined). * \warning This function can only produce righthanded coordinate orientation, not lefthanded. */ virtual void InitializePlane(const Point3D &origin, const Vector3D &normal); /** * \brief Initialize plane by right-/down-vector. * * \warning The vectors are set into the matrix as they are, * \em without normalization! * This function creates a righthanded IndexToWorldTransform, * only a negative thickness could still make it lefthanded. */ void SetMatrixByVectors(const VnlVector &rightVector, const VnlVector &downVector, ScalarType thickness = 1.0); /** - * \brief Change \a transform so that the third column of the - * transform-martix is perpendicular to the first two columns - * + * \brief Check if matrix is a rotation matrix: + * - determinant is 1? + * - R*R^T is ID? + * Output warning otherwise. */ - static void EnsurePerpendicularNormal(AffineTransform3D *transform); + static bool CheckRotationMatrix(AffineTransform3D *transform, double epsilon=mitk::eps); /** * \brief Normal of the plane * */ Vector3D GetNormal() const; /** * \brief Normal of the plane as VnlVector * */ VnlVector GetNormalVnl() const; virtual ScalarType SignedDistance(const Point3D &pt3d_mm) const; /** * \brief Calculates, whether a point is below or above the plane. There are two different *calculation methods, with or without consideration of the bounding box. */ virtual bool IsAbove(const Point3D &pt3d_mm, bool considerBoundingBox = false) const; /** * \brief Distance of the point from the plane * (bounding-box \em not considered) * */ ScalarType DistanceFromPlane(const Point3D &pt3d_mm) const; /** * \brief Signed distance of the point from the plane * (bounding-box \em not considered) * * > 0 : point is in the direction of the direction vector. */ inline ScalarType SignedDistanceFromPlane(const Point3D &pt3d_mm) const { ScalarType len = GetNormalVnl().two_norm(); if (len == 0) return 0; return (pt3d_mm - GetOrigin()) * GetNormal() / len; } /** * \brief Distance of the plane from another plane * (bounding-box \em not considered) * * Result is 0 if planes are not parallel. */ ScalarType DistanceFromPlane(const PlaneGeometry *plane) const { return fabs(SignedDistanceFromPlane(plane)); } /** * \brief Signed distance of the plane from another plane * (bounding-box \em not considered) * * Result is 0 if planes are not parallel. */ inline ScalarType SignedDistanceFromPlane(const PlaneGeometry *plane) const { if (IsParallel(plane)) { return SignedDistance(plane->GetOrigin()); } return 0; } /** * \brief Calculate the intersecting line of two planes * * \return \a true planes are intersecting * \return \a false planes do not intersect */ bool IntersectionLine(const PlaneGeometry *plane, Line3D &crossline) const; /** * \brief Calculate two points where another plane intersects the border of this plane * * \return number of intersection points (0..2). First interection point (if existing) * is returned in \a lineFrom, second in \a lineTo. */ unsigned int IntersectWithPlane2D(const PlaneGeometry *plane, Point2D &lineFrom, Point2D &lineTo) const; /** * \brief Calculate the angle between two planes * * \return angle in radiants */ double Angle(const PlaneGeometry *plane) const; /** * \brief Calculate the angle between the plane and a line * * \return angle in radiants */ double Angle(const Line3D &line) const; /** * \brief Calculate intersection point between the plane and a line * * \param intersectionPoint intersection point * \return \a true if \em unique intersection exists, i.e., if line * is \em not on or parallel to the plane */ bool IntersectionPoint(const Line3D &line, Point3D &intersectionPoint) const; /** * \brief Calculate line parameter of intersection point between the * plane and a line * * \param t parameter of line: intersection point is * line.GetPoint()+t*line.GetDirection() * \return \a true if \em unique intersection exists, i.e., if line * is \em not on or parallel to the plane */ bool IntersectionPointParam(const Line3D &line, double &t) const; /** * \brief Returns whether the plane is parallel to another plane * * @return true iff the normal vectors both point to the same or exactly oposit direction */ bool IsParallel(const PlaneGeometry *plane) const; /** * \brief Returns whether the point is on the plane * (bounding-box \em not considered) */ bool IsOnPlane(const Point3D &point) const; /** * \brief Returns whether the line is on the plane * (bounding-box \em not considered) */ bool IsOnPlane(const Line3D &line) const; /** * \brief Returns whether the plane is on the plane * (bounding-box \em not considered) * * @return true iff the normal vector of the planes point to the same or the exactly oposit direction and * the distance of the planes is < eps * */ bool IsOnPlane(const PlaneGeometry *plane) const; /** * \brief Returns the lot from the point to the plane */ Point3D ProjectPointOntoPlane(const Point3D &pt) const; itk::LightObject::Pointer InternalClone() const override; /** Implements operation to re-orient the plane */ void ExecuteOperation(Operation *operation) override; /** * \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D * geometry. The result is a 2D point in mm (\a pt2d_mm). * * The result is a 2D point in mm (\a pt2d_mm) relative to the upper-left * corner of the geometry. To convert this point into units (e.g., pixels * in case of an image), use WorldToIndex. * \return true projection was possible * \sa Project(const mitk::Point3D &pt3d_mm, mitk::Point3D * &projectedPt3d_mm) */ virtual bool Map(const mitk::Point3D &pt3d_mm, mitk::Point2D &pt2d_mm) const; /** * \brief Converts a 2D point given in mm (\a pt2d_mm) relative to the * upper-left corner of the geometry into the corresponding * world-coordinate (a 3D point in mm, \a pt3d_mm). * * To convert a 2D point given in units (e.g., pixels in case of an * image) into a 2D point given in mm (as required by this method), use * IndexToWorld. */ virtual void Map(const mitk::Point2D &pt2d_mm, mitk::Point3D &pt3d_mm) const; /** * \brief Set the width and height of this 2D-geometry in units by calling * SetBounds. This does \a not change the extent in mm! * * For an image, this is the number of pixels in x-/y-direction. * \note In contrast to calling SetBounds directly, this does \a not change * the extent in mm! */ virtual void SetSizeInUnits(mitk::ScalarType width, mitk::ScalarType height); /** * \brief Project a 3D point given in mm (\a pt3d_mm) onto the 2D * geometry. The result is a 3D point in mm (\a projectedPt3d_mm). * * \return true projection was possible */ virtual bool Project(const mitk::Point3D &pt3d_mm, mitk::Point3D &projectedPt3d_mm) const; /** * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D * geometry. The result is a 2D vector in mm (\a vec2d_mm). * * The result is a 2D vector in mm (\a vec2d_mm) relative to the * upper-left * corner of the geometry. To convert this point into units (e.g., pixels * in case of an image), use WorldToIndex. * \return true projection was possible * \sa Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D * &projectedVec3d_mm) */ virtual bool Map(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector2D &vec2d_mm) const; /** * \brief Converts a 2D vector given in mm (\a vec2d_mm) relative to the * upper-left corner of the geometry into the corresponding * world-coordinate (a 3D vector in mm, \a vec3d_mm). * * To convert a 2D vector given in units (e.g., pixels in case of an * image) into a 2D vector given in mm (as required by this method), use * IndexToWorld. */ virtual void Map(const mitk::Point2D &atPt2d_mm, const mitk::Vector2D &vec2d_mm, mitk::Vector3D &vec3d_mm) const; /** * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D * geometry. The result is a 3D vector in mm (\a projectedVec3d_mm). * * DEPRECATED. Use Project(vector,vector) instead * * \return true projection was possible */ virtual bool Project(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; /** * \brief Project a 3D vector given in mm (\a vec3d_mm) onto the 2D * geometry. The result is a 3D vector in mm (\a projectedVec3d_mm). * * \return true projection was possible */ virtual bool Project(const mitk::Vector3D &vec3d_mm, mitk::Vector3D &projectedVec3d_mm) const; /** * \brief Distance of the point from the geometry * (bounding-box \em not considered) * */ inline ScalarType Distance(const Point3D &pt3d_mm) const { return fabs(SignedDistance(pt3d_mm)); } /** * \brief Set the geometrical frame of reference in which this PlaneGeometry * is placed. * * This would usually be the BaseGeometry of the underlying dataset, but * setting it is optional. */ void SetReferenceGeometry(const mitk::BaseGeometry *geometry); /** * \brief Get the geometrical frame of reference for this PlaneGeometry. */ const BaseGeometry *GetReferenceGeometry() const; bool HasReferenceGeometry() const; + static std::vector< int > CalculateDominantAxes(mitk::AffineTransform3D::MatrixType::InternalMatrixType& rotation_matrix); + protected: PlaneGeometry(); PlaneGeometry(const PlaneGeometry &other); ~PlaneGeometry() override; void PrintSelf(std::ostream &os, itk::Indent indent) const override; const mitk::BaseGeometry *m_ReferenceGeometry; //##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); }; //##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!) void CheckBounds(const BoundsArrayType &bounds) override; //##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);. void CheckIndexToWorldTransform(mitk::AffineTransform3D *transform) override; private: /** * \brief Compares plane with another plane: \a true if IsOnPlane * (bounding-box \em not considered) */ virtual bool operator==(const PlaneGeometry *) const { return false; }; /** * \brief Compares plane with another plane: \a false if IsOnPlane * (bounding-box \em not considered) */ virtual bool operator!=(const PlaneGeometry *) const { return false; }; }; } // namespace mitk #endif /* PLANEGEOMETRY_H_HEADER_INCLUDED_C1C68A2C */ diff --git a/Modules/Core/include/mitkPropertyRelationRuleBase.h b/Modules/Core/include/mitkPropertyRelationRuleBase.h index 3d5e652cba..30ed8d9bab 100644 --- a/Modules/Core/include/mitkPropertyRelationRuleBase.h +++ b/Modules/Core/include/mitkPropertyRelationRuleBase.h @@ -1,339 +1,341 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkPropertyRelationRuleBase_h #define mitkPropertyRelationRuleBase_h #include "mitkIPropertyOwner.h" #include "mitkIdentifiable.h" #include "mitkException.h" #include "mitkNodePredicateBase.h" #include "mitkPropertyKeyPath.h" #include #include namespace mitk { /**Base class to standardize/abstract/encapsulate rules and business logic to detect and define (property/data based) relations in MITK. Following important definitions must be regarded when using/implementing/specifing rule classes: - Releations represented by rules are directed relations that point from a source IPropertyOwner (Source) to a destination IPropertyOwner (Destination). - Rule can be abstract (indicated by IsAbstract()) or concrete. Abstract rules cannot be used to connect relations. Abstract rules can only be used to detect/indicate or disconnect relations. Therefore, in contrast to concrete rules, abstract rules can be used to indicate several relations that are established be "derived" rules. See e.g. GenericIDRelationRule: in its abstract state it cannot connect but be used to detect any type of generic ID relation. - A concrete rule ID (rule ID of a concrete rule) always "implements" a concrete relation type. E.g. In DICOM the way to express the source image relation to an input image and to a mask would be nearly the same and only differs by the encoded purpose. One may implement an interim or joined class that manages the mutual stuff, but the registered instances must be one concrete rule for "DICOM source input image" and one concrete rule for "DICOM source mask" and both rules must have distinct rule IDs. - Source may have several relations of a rule to different Destinations. Destination may have several relations of a rule from different Sources. But a specific source destination pair may have only one relation of a specific rule id (concrete rule). A specific source destination pair may however have multiple relations for an abstract rule. - The deletion of a Destination in the storage does not remove the relation implicitly. It becomes a "zombie" relation but it should still be documented, even if the destination is unknown. One has to explicitly disconnect a zombie relation to get rid of it. - Each relation has its own UID (relationUID) that can be used to address it. The basic concept of the rule design is that we have two layers of relation identification: Layer 1 is the ID-layer which uses the IIdentifiable interface and UIDs if available to encode "hard" relations. Layer 2 is the Data-layer which uses the properties of Source and Destination to deduce if there is a relation of the rule type. The ID-layer is completely implemented by this base class. The base class only falls back to the Data-layer (implemented by the concrete rule class) if the ID-layer is not sufficient or it is explicitly stated. Reasons for the introduction of the ID-layer are: 1st, data-defined relations may be weak (several Destinations are possible; e.g. DICOM source images may point to several loaded mitk images). But if explicitly a relation was connected it should be deduceable. 2nd, checks on a UID are faster then unnecessary data deduction. Rules use relation instance identifing (RII) properties in order to manage their relations that are stored in the Source. The RII-properties follow the following naming schema: "MITK.Relations...[relationUID|destinationUID|ruleID|]" - : The unique index of the relation for the Source. Used to assign/group the properties to their relation. In the default implementation of this class the instance id is an positive integer (i>0). - relationUID: The UID of the relation. Set by the ID-layer (so by this class) - destinationUID: The UID of the Destination. Set by the ID-layer (so by this class) if Destination implements IIdentifiable. - ruleID: The identifier of the concrete rule that sets the property. Is specified by the derived class and set automaticaly be this base class. - : Information needed by the Data-layer (so derived classes) to find the relationUID */ class MITKCORE_EXPORT PropertyRelationRuleBase : public itk::Object { public: mitkClassMacroItkParent(PropertyRelationRuleBase, itk::Object); itkCloneMacro(Self); itkCreateAnotherMacro(Self); using RuleIDType = std::string; using RelationUIDType = Identifiable::UIDType; using RelationUIDVectorType = std::vector; /** Enum class for different types of relations. */ enum class RelationType { None = 0, /**< Two IPropertyOwner have no relation under the rule.*/ Implicit_Data = 1, /**< Two IPropertyOwner have a relation, but it is only deduced from the Data-layer and they were never explicitly connected.*/ Connected_Data = 2, /**< Two IPropertyOwner have a relation and there where connected on the Data-layer (so a bit "weaker" as ID). Reasons for the missing ID connection could be that Destintination has not IIdentifiable implemented.*/ Connected_ID = 4 /**< Two IPropertyOwner have a relation and are fully explictly connected.*/ }; /** Returns the generic root path for relation rules ("MITK.Relations").*/ static PropertyKeyPath GetRootKeyPath(); /** Returns an ID string that identifies the rule class. @post The returned rule ID must met the preconditions of a PropertyKeyPath element name (see mitk::PropertyKeyPath*/ virtual RuleIDType GetRuleID() const = 0; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ virtual std::string GetDisplayName() const = 0; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ virtual std::string GetSourceRoleName() const = 0; /** Returns a human readable string that can be used to describe the role of a destionation in context of the rule * instance.*/ virtual std::string GetDestinationRoleName() const = 0; /** Returns if the instance is a abstract rule (true). Default implementation is true. Overwrite and reimplement if another behavior is needed.*/ virtual bool IsAbstract() const; /** This method checks if owner is eligible to be a Source for the rule. The default implementation returns a True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if they have requirements on potential Sources).*/ virtual bool IsSourceCandidate(const IPropertyProvider *owner) const; /** This method checks if owner is eligible to be a Destination for the rule. The default implementation returns a True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if they have requirements on potential Sources).*/ virtual bool IsDestinationCandidate(const IPropertyProvider *owner) const; /** Returns true if the passed owner is a Source of a relation defined by the rule so has at least one relation of type Connected_Data or Connected_ID. @pre owner must be a pointer to a valid IPropertyProvider instance.*/ bool IsSource(const IPropertyProvider *owner) const; /** Returns the relation type of the passed IPropertyOwner instances. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance. */ RelationType HasRelation(const IPropertyProvider *source, const IPropertyProvider *destination) const; /** Returns a vector of relation UIDs for all relations of this rule instance that are defined for the passed source (so relations of type Connected_Data and Connected_ID). @pre source must be a pointer to a valid IPropertyOwner instance. */ RelationUIDVectorType GetExistingRelations(const IPropertyProvider *source) const; /** Returns the relation UID(s) for the passed source and destination of this rule instance. If the rule is abstract multiple relation UIDs might be returned. In case of concrete rule only one relation UID. If the passed instances have no explicit relation (so of type Connected_Data or Connected_ID), no ID can be deduced and an empty relation UID vector is returned. @pre source must be a pointer to a valid IPropertyOwner instance. @pre destination must be a pointer to a valid IPropertyOwner instance.*/ RelationUIDVectorType GetRelationUIDs(const IPropertyProvider *source, const IPropertyProvider *destination) const; /** Returns the relation UID for the passed source and destination of this rule instance. If the passed instances have no explicit relation (so of type Connected_Data or Connected_ID), no ID can be deduced and an exception will be thrown. If more then one relation is found, also an exception will be thrown. Thus only use this convinience method, if you are sure that one(!) relation UID can exist. @pre source must be a pointer to a valid IPropertyOwner instance. @pre destination must be a pointer to a valid IPropertyOwner instance. @pre Source and destination have one relation of type Connected_Data or Connected_ID; otherwise if no relation exists a NoPropertyRelationException is thrown; if more than one relation exists a default MITK expception is thrown.*/ RelationUIDType GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const; /**Predicate that can be used to find nodes that qualify as source for that rule (but must not be a source yet). Thus all nodes where IsSourceCandidate() returns true. */ NodePredicateBase::ConstPointer GetSourceCandidateIndicator() const; /**Predicate that can be used to find nodes that qualify as destination for that rule (but must not be a destination yet). Thus all nodes where IsDestinationCandidate() returns true. */ NodePredicateBase::ConstPointer GetDestinationCandidateIndicator() const; /**Predicate that can be used to find nodes that are Sources of that rule and explicitly connected. Thus all nodes where IsSource() returns true.*/ NodePredicateBase::ConstPointer GetConnectedSourcesDetector() const; /**Predicate that can be used to find nodes that are as source related to the passed Destination under the rule @param destination Pointer to the Destination instance that should be used for detection. @param minimalRelation Defines the minimal strength of the relation type that should be detected. @pre Destination must be a valid instance.*/ NodePredicateBase::ConstPointer GetSourcesDetector( const IPropertyProvider *destination, RelationType minimalRelation = RelationType::Implicit_Data) const; /**Predicate that can be used to find nodes that are as Destination related to the passed Source under the rule @param source Pointer to the Source instance that should be used for detection. @param minimalRelation Defines the minimal strength of the relation type that should be detected. @pre Destination must be a valid instance.*/ NodePredicateBase::ConstPointer GetDestinationsDetector( const IPropertyProvider *source, RelationType minimalRelation = RelationType::Implicit_Data) const; /**Returns a predicate that can be used to find the Destination of the passed Source for a given relationUID. @param source Pointer to the Source instance that should be used for detection. @param minimalRelation Defines the minimal strength of the relation type that should be detected. @pre source must be a valid instance. @pre relationUID must identify a relation of the passed source and rule. (This must be in the return of this->GetExistingRelations(source). */ NodePredicateBase::ConstPointer GetDestinationDetector(const IPropertyProvider *source, RelationUIDType relationUID) const; /**Disconnect the passed instances. Afterwards they have a relation of None or Implicit_Data. All RII-properties in the source for the passed destination will be removed. @pre source must be a valid instance. @pre destination must be a valid instance.*/ void Disconnect(IPropertyOwner *source, const IPropertyProvider *destination) const; /**Disconnect the source from the passed relationUID (usefull for "zombie relations"). All RII-properties in the source for the passed relationUID will be removed. If the relationUID is not part of the source. Nothing will be changed. @pre source must be a valid instance.*/ void Disconnect(IPropertyOwner *source, RelationUIDType relationUID) const; protected: PropertyRelationRuleBase() = default; ~PropertyRelationRuleBase() override = default; using InstanceIDType = std::string; using InstanceIDVectorType = std::vector; static InstanceIDType NULL_INSTANCE_ID(); /** Returns the instance IDs for the passed source and destination for this rule instance. If the passed source and destination instances has no explicit relation on the ID layer (Connected_ID), an empty vector will be returned. @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider pair and concrete rule. But for there might be more then one instanceID, because the rule is abstract and supports multiple rule IDs. 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance could match. The implementation of this function should report all relation instances. The calling function will take care. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance.*/ InstanceIDVectorType GetInstanceID_IDLayer(const IPropertyProvider *source, const IPropertyProvider *destination) const; /** Is called if an instance ID cannot be deduced on the ID-layer. Implement this method to check which existing relation(s) as Connected_Data exists between both passed instances. If the passed instances have no explicit relation of type Connected_Data, an empty vector will be returned. @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider pair and concrete rule. But for 2 reasons there might be more then one instanceID: 1) the rule is abstract and supports multiple rule IDs. 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance could match. The implementation of this function should report all relation instances. The calling function will take care. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance.*/ virtual InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, const IPropertyProvider *destination) const = 0; /** Is called by HasRelation() if no relation of type Connected_ID (GetInstanceID_IDLayer()) or Connected_Data (GetInstanceID_datalayer()) is evident. Implement this method to deduce if the passed instances have a relation of type Implicit_Data. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance. */ virtual bool HasImplicitDataRelation(const IPropertyProvider *source, const IPropertyProvider *destination) const = 0; /**Helper function that deduces the relation UID of the given relation instance. If it cannot be deduced an NoPropertyRelationException is thrown.*/ RelationUIDType GetRelationUIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const; /**Helper function that deduces the relation instance ID given the relation UID. If it cannot be deduced an NoPropertyRelationException is thrown.*/ InstanceIDType GetInstanceIDByRelationUID(const IPropertyProvider *source, const RelationUIDType &relationUID) const; /**Explicitly connects the passed instances. Afterwards they have a relation of Connected_Data or Connected_ID (if a destination implements IIdentifiable). If the passed instance are already connected the old connection will be overwritten (and raised to the highest possible connection level). @remark This method has protected visibility in the base implementation, because it is a design decision of derived rule classes which interface they want to offer for connecting. It may just be made public (e.g. GenericIDRelationRule) or used by own implementations. @pre source must be a valid instance. @pre destination must be a valid instance. @pre the rule instance must not be abstract. @return Return the relation uid of the relation connected by this method call*/ RelationUIDType Connect(IPropertyOwner *source, const IPropertyProvider *destination) const; /**Is called by Connect() to ensure that source has correctly set properties to resemble the relation on the data layer. This means that the method should set the properties that describe and encode the relation on the data layer (data-layer-specific relation properties). If the passed instance are already connected, the old settings should be overwritten. Connect() will ensure that source and destination are valid pointers. @param instanceID is the ID for the relation instance that should be connected. Existance of the relation instance is ensured. @pre source must be a valid instance. @pre destination must be a valid instance.*/ virtual void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const = 0; /**This method is called by Disconnect() to remove all properties of the relation from the source that are set by Connect_datalayer(). @remark All RII-properties of this relation will removed by Disconnect() after this method call. If the relationUID is not part of the source. Nothing will be changed. Disconnect() ensures that source is a valid pointer if called. @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/ virtual void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType &instanceID) const = 0; /** Returns if the passed rule ID is supported/relevant for the rule. Either because it is the very ID of the rule (default implementation) or because it is an abstract rule which also supports the rule ID. @return true: If the rule ID can handle the rule ID. false: the rule does not support the rule ID.*/ virtual bool IsSupportedRuleID(const RuleIDType& ruleID) const; /** Helper function that generates a reg ex that can be used to find a specific RII property for the rule instance. * @param propName If not empty a PropertyPath element will added (with the passed value) after the element. * @param instanceID If not empty only for the reg ex will only valid for the passed instanceID. Otherwise for all.*/ std::string GetRIIPropertyRegEx(const std::string propName = "", const InstanceIDType &instanceID = "") const; /**Helper function that deduces the instance ID out of a property name. If it cannot be deduced an MITK exception is thrown.*/ static InstanceIDType GetInstanceIDByPropertyName(const std::string propName); /**Helper function that retrives the rule ID of a relation instance of a passed source. @pre source must be valid. @pre source must have a relation instance with this ID*/ RuleIDType GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const; itk::LightObject::Pointer InternalClone() const override; + static std::vector GetPropertyKeys(const mitk::IPropertyProvider *owner); + private: /** Creats a relation UID*/ static RelationUIDType CreateRelationUID(); /**Prepares a new relation instance. Therefore an unused and valid instance ID for the passed source will be genarated and a relationUID property with the relationUID will be set to block the instance ID. The instance ID will be returned. @remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently.*/ InstanceIDType CreateNewRelationInstance(IPropertyOwner *source, const RelationUIDType &relationUID) const; }; /**Exception that is used by PropertyRelationRuleBase based classes to indicate that two objects have no relation.*/ class NoPropertyRelationException : public Exception { public: mitkExceptionClassMacro(NoPropertyRelationException, Exception) }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkSourceImageRelationRule.h b/Modules/Core/include/mitkSourceImageRelationRule.h new file mode 100644 index 0000000000..b631e5b3d8 --- /dev/null +++ b/Modules/Core/include/mitkSourceImageRelationRule.h @@ -0,0 +1,164 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkGenericIDRelationRule_h +#define mitkGenericIDRelationRule_h + +#include "mitkPropertyRelationRuleBase.h" +#include "mitkImage.h" + +namespace mitk +{ + /**This rule class can be used for relations that reference an image as source for a destination entity. + (e.g. an image that is used to generate the relation source). + The ID-layer is supported like for GenericIDReleations. + So it can be used for all ID based relations between PropertyProviders + that also implement the interface identifiable. + In addition the rule uses the data-layer to deduce/define relations. + For this layer it uses properties compliant to DICOM. Thus (1) the information is stored in + a DICOM Source Image Sequence item (0x0008,0x2112) and (2) the destination must have properties + DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016). If the destination + does not have this properties, no connection can be made on the data-layer. + @remark Please note that PropertyRelationRules and DICOM use the term "source" differently. The DICOM + source (image) equals the PropertyRelationRule destination. This is due to + an inverted relation direction. So in the context of the SourceImageRelationRule interface a derived data is + the source and points to the original image, it derives from. In the context of DICOM this referenced original image would be + called source image (as the name of this class). + In order to be able to use this class for different relation types (DICOM would call it purposes), + the purposeTag is used. It must be specified when creating a rule instance. + The purposeTag will be used as suffix for the rule ID of the instance and therefore allows to create + specific and distinguishable rules instances based on this class. + One may also specify the display name and the role names of the instance. If not specified the default values + are used (display name: " relation", source role name: "derived data", + destination role name: "source image") + */ + class MITKCORE_EXPORT SourceImageRelationRule : public mitk::PropertyRelationRuleBase + { + public: + mitkClassMacro(SourceImageRelationRule, PropertyRelationRuleBase); + itkNewMacro(Self); + mitkNewMacro1Param(Self, const RuleIDType &); + mitkNewMacro2Param(Self, const RuleIDType &, const std::string &); + mitkNewMacro4Param(Self, const RuleIDType &, const std::string &, const std::string &, const std::string &); + + using RuleIDType = PropertyRelationRuleBase::RuleIDType; + using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; + using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; + + /** Returns an ID string that identifies the rule class */ + RuleIDType GetRuleID() const override; + + bool IsAbstract() const override; + + /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ + std::string GetDisplayName() const override; + + /** Returns a human readable string that can be used to describe the role of a source in context of the rule + * instance.*/ + std::string GetSourceRoleName() const override; + /** Returns a human readable string that can be used to describe the role of a destination in context of the rule + * instance.*/ + std::string GetDestinationRoleName() const override; + + bool IsDestinationCandidate(const IPropertyProvider *owner) const override; + + /** Connects to passed images. + @remark destination must specifiy DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016) + in order to establish a connection on the data layer.*/ + RelationUIDType Connect(Image *source, const Image *destination) const; + + protected: + SourceImageRelationRule(); + SourceImageRelationRule(const RuleIDType &purposeTag); + SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName); + SourceImageRelationRule(const RuleIDType &purposeTag, + const std::string &displayName, + const std::string &sourceRole, + const std::string &destinationRole); + ~SourceImageRelationRule() override = default; + + using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; + using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; + + /** Helper function that returns a vector of all selections of the property DICOM.0008.2112 that refer to destination or all (if no destination is passed).*/ + std::vector GetReferenceSequenceIndices(const IPropertyProvider * source, + const IPropertyProvider * destination = nullptr) const; + + /** Is called if a instance ID cannot be deduced on the ID-layer. + Implement this method to check which existing relation(s) as Connected_Data exists between + both passed instances. If the passed instances have no + explicit relation of type Connected_Data, an empty vector will be returned. + @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider + pair and rule. But the data layer may be ambiguous and there for muliple relation instances of the rule instance + could match. The implementation of this function should report all relation instances. The calling function + will take care of this violation. + @pre source must be a pointer to a valid IPropertyProvider instance. + @pre destination must be a pointer to a valid IPropertyProvider instance.*/ + InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, + const IPropertyProvider *destination) const override; + + /** Is called by HasRelation() if no relation of type Connected_ID (GetInstanceID_IDLayer()) or + Connected_Data (GetInstanceID_datalayer()) is evident. + Implement this method to deduce if the passed instances have a relation of type + Implicit_Data. + @pre source must be a pointer to a valid IPropertyProvider instance. + @pre destination must be a pointer to a valid IPropertyProvider instance. + */ + bool HasImplicitDataRelation(const IPropertyProvider *source, + const IPropertyProvider *destination) const override; + + /**Is called by Connect() to ensure that source has correctly set properties to resemble + the relation on the data layer. This means that the method should set the properties that describe + and encode the relation on the data layer (data-layer-specific relation properties). + If the passed instance are already connected, the old settings should be + overwritten. Connect() will ensure that source and destination are valid pointers. + @param instanceID is the ID for the relation instance that should be connected. Existance of the relation instance + is ensured. + @pre source must be a valid instance. + @pre destination must be a valid instance.*/ + void Connect_datalayer(IPropertyOwner *source, + const IPropertyProvider *destination, + const InstanceIDType &instanceID) const override; + + /**This method is called by Disconnect() to remove all properties of the relation from the source that + are set by Connect_datalayer(). + @remark All RII-properties of this relation will removed by Disconnect() after this method call. + If the relationUID is not part of the source. Nothing will be changed. Disconnect() ensures that source is a valid + pointer if called. + @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/ + void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType &instanceID) const override; + + virtual bool IsSupportedRuleID(const RuleIDType& ruleID) const override; + + itk::LightObject::Pointer InternalClone() const override; + + /**Prepares a new reference to an image on the data layer. Therefore an unused and valid sequence item index + for the passed source will be genarated and a relationUID property with the relationUID will be set to block the instance ID. The + instance ID will be returned. + @remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used + concurrently.*/ + PropertyKeyPath::ItemSelectionIndex CreateNewSourceImageSequenceItem(IPropertyOwner *source) const; + + private: + RuleIDType m_PurposeTag; + std::string m_DisplayName; + std::string m_SourceRole; + std::string m_DestinationRole; + }; + +} // namespace mitk + +#endif diff --git a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp index 08c201f25f..740638f157 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindowManager.cpp @@ -1,624 +1,741 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkLevelWindowManager.h" #include "mitkDataStorage.h" #include "mitkImage.h" #include "mitkMessage.h" #include "mitkNodePredicateAnd.h" #include "mitkNodePredicateBase.h" #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateProperty.h" #include "mitkProperties.h" #include "mitkRenderingModeProperty.h" #include mitk::LevelWindowManager::LevelWindowManager() - : m_DataStorage(nullptr), - m_LevelWindowProperty(nullptr), - m_AutoTopMost(true), - m_IsObserverTagSet(false), - m_CurrentImage(nullptr), - m_IsPropertyModifiedTagSet(false), - m_SettingImgForLvlWinProp(false) + : m_DataStorage(nullptr) + , m_LevelWindowProperty(nullptr) + , m_AutoTopMost(true) + , m_SelectedImagesMode(false) + , m_IsObserverTagSet(false) + , m_CurrentImage(nullptr) + , m_IsPropertyModifiedTagSet(false) + , m_LevelWindowMutex(false) { } mitk::LevelWindowManager::~LevelWindowManager() { if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( - MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); + MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( - MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); + MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); m_DataStorage = nullptr; } if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } // clear both observer maps this->ClearPropObserverLists(); } -void mitk::LevelWindowManager::SetDataStorage(mitk::DataStorage *ds) +void mitk::LevelWindowManager::SetDataStorage(DataStorage *ds) { if (ds == nullptr) return; /* remove listeners of old DataStorage */ if (m_DataStorage.IsNotNull()) { m_DataStorage->AddNodeEvent.RemoveListener( - MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); + MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.RemoveListener( - MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); + MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); } /* register listener for new DataStorage */ m_DataStorage = ds; // register m_DataStorage->AddNodeEvent.AddListener( - MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); + MessageDelegate1(this, &LevelWindowManager::DataStorageAddedNode)); m_DataStorage->RemoveNodeEvent.AddListener( - MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); + MessageDelegate1(this, &LevelWindowManager::DataStorageRemovedNode)); this->DataStorageAddedNode(); // update us with new DataStorage } -void mitk::LevelWindowManager::OnPropertyModified(const itk::EventObject &) +mitk::DataStorage *mitk::LevelWindowManager::GetDataStorage() { - Modified(); + return m_DataStorage.GetPointer(); } -void mitk::LevelWindowManager::SetAutoTopMostImage(bool autoTopMost, const mitk::DataNode *removedNode) +void mitk::LevelWindowManager::SetAutoTopMostImage(bool autoTopMost, const DataNode *removedNode/* = nullptr*/) { m_AutoTopMost = autoTopMost; - if (m_AutoTopMost == false) + if (false == m_AutoTopMost) + { return; + } + + // deactivate other mode + m_SelectedImagesMode = false; if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } - /* search topmost image */ + // find topmost image in the data storage if (m_DataStorage.IsNull()) { - itkExceptionMacro("DataStorage not set"); + mitkThrow() << "DataStorage not set"; } - int maxLayer = itk::NumericTraits::min(); + DataNode::Pointer topLevelNode; + int maxVisibleLayer = itk::NumericTraits::min(); m_LevelWindowProperty = nullptr; + m_CurrentImage = nullptr; - mitk::DataNode::Pointer topLevelNode; - - mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); - for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) + DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); + for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { - mitk::DataNode::Pointer node = it->Value(); - if (node.IsNull() || (removedNode != nullptr && node == removedNode)) + DataNode::Pointer node = it->Value(); + if (node.IsNull() || node == removedNode) + { continue; + } - m_SettingImgForLvlWinProp = true; + m_LevelWindowMutex = true; node->SetBoolProperty("imageForLevelWindow", false); - m_SettingImgForLvlWinProp = false; + m_LevelWindowMutex = false; - if (node->IsVisible(nullptr) == false) + if (false == node->IsVisible(nullptr)) + { continue; + } - int layer = 0; + int layer = -1; node->GetIntProperty("layer", layer); - if (layer < maxLayer) + if (layer < maxVisibleLayer) + { + continue; + } + + bool ignore = this->IgnoreNode(node); + if (ignore) + { + continue; + } + + m_LevelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); + topLevelNode = node; + maxVisibleLayer = layer; + } + + // this will set the "imageForLevelWindow" property and the 'm_CurrentImage' and call 'Modified()' + this->SetLevelWindowProperty(m_LevelWindowProperty); + + if (m_LevelWindowProperty.IsNull()) + { + this->Modified(); + } +} + +void mitk::LevelWindowManager::SetSelectedImages(bool selectedImagesMode, const DataNode *removedNode/* = nullptr*/) +{ + m_SelectedImagesMode = selectedImagesMode; + if (false == m_SelectedImagesMode) + { + return; + } + + // deactivate other mode + m_AutoTopMost = false; + + if (m_IsPropertyModifiedTagSet && m_LevelWindowProperty.IsNotNull()) + { + m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); + m_IsPropertyModifiedTagSet = false; + } + + // find selected images in the data storage + if (m_DataStorage.IsNull()) + { + mitkThrow() << "DataStorage not set"; + } + + DataNode::Pointer lastSelectedNode; + m_LevelWindowProperty = nullptr; + m_CurrentImage = nullptr; + + DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); + for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) + { + DataNode::Pointer node = it->Value(); + if (node.IsNull() || node == removedNode) + { continue; + } + + m_LevelWindowMutex = true; + node->SetBoolProperty("imageForLevelWindow", false); + m_LevelWindowMutex = false; - mitk::LevelWindowProperty::Pointer levelWindowProperty = - dynamic_cast(node->GetProperty("levelwindow")); - if (levelWindowProperty.IsNull()) + if (false == node->IsSelected()) + { continue; + } - int nonLvlWinMode1 = mitk::RenderingModeProperty::LOOKUPTABLE_COLOR; - int nonLvlWinMode2 = mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR; + bool ignore = this->IgnoreNode(node); + if (ignore) + { + continue; + } - mitk::RenderingModeProperty::Pointer mode = - dynamic_cast(node->GetProperty("Image Rendering.Mode")); + m_LevelWindowProperty = dynamic_cast(node->GetProperty("levelwindow")); + m_RelevantDataNodes.push_back(node); + lastSelectedNode = node; + } + + // this will set the "imageForLevelWindow" property and the 'm_CurrentImage' and call 'Modified()' + this->SetLevelWindowProperty(m_LevelWindowProperty); + + if (m_LevelWindowProperty.IsNull()) + { + this->Modified(); + } +} - if (mode.IsNotNull()) +void mitk::LevelWindowManager::RecalculateLevelWindowForSelectedComponent(const itk::EventObject &event) +{ + DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); + for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) + { + DataNode::Pointer node = it->Value(); + if (node.IsNull()) + continue; + + bool isSelected = false; + node->GetBoolProperty("selected", isSelected); + if (isSelected) { - int currMode = mode->GetRenderingMode(); - if (currMode == nonLvlWinMode1 || currMode == nonLvlWinMode2) - { - continue; + LevelWindow selectedLevelWindow; + node->GetLevelWindow(selectedLevelWindow); // node is an image node because of predicates + + auto *image = dynamic_cast(node->GetData()); + int displayedComponent = 0; + if (image && (node->GetIntProperty("Image.Displayed Component", displayedComponent))) + { // we found a selected image with a displayed component + // let's recalculate the levelwindow for this. + selectedLevelWindow.SetAuto(image, true, true, static_cast(displayedComponent)); + node->SetLevelWindow(selectedLevelWindow); } } - else + + LevelWindow levelWindow; + node->GetLevelWindow(levelWindow); + } + + this->Update(event); +} + +void mitk::LevelWindowManager::Update(const itk::EventObject &) +{ + if (m_LevelWindowMutex) // no mutex, should still help + { + return; + } + + m_RelevantDataNodes.clear(); + if (m_AutoTopMost) + { + this->SetAutoTopMostImage(true); + return; + } + + if (m_SelectedImagesMode) + { + this->SetSelectedImages(true); + return; + } + + int maxVisibleLayer = itk::NumericTraits::min(); + DataNode::Pointer topLevelNode = nullptr; + std::vector nodesForLevelWindow; + + DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); + for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) + { + DataNode::Pointer node = it->Value(); + + if (node.IsNull()) + { continue; + } - m_LevelWindowProperty = levelWindowProperty; - m_CurrentImage = dynamic_cast(node->GetData()); - topLevelNode = node; + if (node->IsVisible(nullptr) == false) + { + continue; + } + + bool prop = false; + node->GetBoolProperty("imageForLevelWindow", prop); + if (prop) + { + nodesForLevelWindow.push_back(node); + continue; + } - maxLayer = layer; + int layer = -1; + node->GetIntProperty("layer", layer); + if (layer > maxVisibleLayer) + { + // top level node is backup node, if no node with + // "imageForLevelWindow" property with value "true" is found + topLevelNode = node; + maxVisibleLayer = layer; + } } - if (topLevelNode.IsNotNull()) + int nodesForLevelWindowSize = nodesForLevelWindow.size(); + if (nodesForLevelWindowSize > 2) { - m_SettingImgForLvlWinProp = true; - topLevelNode->SetBoolProperty("imageForLevelWindow", true); - m_SettingImgForLvlWinProp = false; + MITK_ERROR << "Error: not more than two visible nodes are expected to have the imageForLevelWindow property set at " + "any point."; } - this->SetLevelWindowProperty(m_LevelWindowProperty); + if (nodesForLevelWindowSize > 0) + { + // 1 or 2 nodes for level window found + for (const auto& node : nodesForLevelWindow) + { + LevelWindowProperty::Pointer newProp = dynamic_cast(node->GetProperty("levelwindow")); + if (newProp != m_LevelWindowProperty) + { + this->SetLevelWindowProperty(newProp); + return; + } + } + } + else if (topLevelNode) + { + // no nodes for level window found + LevelWindowProperty::Pointer lvlProp = dynamic_cast(topLevelNode->GetProperty("levelwindow")); + this->SetLevelWindowProperty(lvlProp); + } + else + { + // no nodes for level window found and no visible top level node found + this->Modified(); + } +} - if (m_LevelWindowProperty.IsNull()) +void mitk::LevelWindowManager::UpdateSelected(const itk::EventObject &) +{ + if (m_LevelWindowMutex) // no mutex, should still help { - Modified(); + return; + } + + m_RelevantDataNodes.clear(); + if (m_SelectedImagesMode) + { + this->SetSelectedImages(true); } - // else SetLevelWindowProperty will call Modified(); } -// sets an specific LevelWindowProperty, all changes will affect the image belonging to this property. void mitk::LevelWindowManager::SetLevelWindowProperty(LevelWindowProperty::Pointer levelWindowProperty) { if (levelWindowProperty.IsNull()) + { return; + } - /* search image than belongs to the property */ - typedef mitk::DataStorage::SetOfObjects NodeSetType; - NodeSetType::ConstPointer nodes = m_DataStorage->GetAll(); - NodeSetType::ConstIterator it = nodes->Begin(); + // find data node that belongs to the property + DataStorage::SetOfObjects::ConstPointer all = m_DataStorage->GetAll(); mitk::DataNode::Pointer propNode = nullptr; - while (it != nodes->End()) + for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { - mitk::DataNode::Pointer node = it.Value(); - mitk::LevelWindowProperty::Pointer prop = - dynamic_cast(node->GetProperty("levelwindow")); - if (prop == levelWindowProperty) + DataNode::Pointer node = it.Value(); + LevelWindowProperty::Pointer property = dynamic_cast(node->GetProperty("levelwindow")); + if (property == levelWindowProperty) { propNode = node; } else { - m_SettingImgForLvlWinProp = true; + m_LevelWindowMutex = true; node->SetBoolProperty("imageForLevelWindow", false); - m_SettingImgForLvlWinProp = false; + m_LevelWindowMutex = false; } - - ++it; } if (propNode.IsNull()) { - mitkThrow() << "No Image in DataStorage that belongs to LevelWindow property" << m_LevelWindowProperty; + mitkThrow() << "No Image in the data storage that belongs to level-window property " << m_LevelWindowProperty; } if (m_IsPropertyModifiedTagSet) // remove listener for old property { m_LevelWindowProperty->RemoveObserver(m_PropertyModifiedTag); m_IsPropertyModifiedTagSet = false; } m_LevelWindowProperty = levelWindowProperty; itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); // register listener for new property command->SetCallbackFunction(this, &LevelWindowManager::OnPropertyModified); m_PropertyModifiedTag = m_LevelWindowProperty->AddObserver(itk::ModifiedEvent(), command); m_IsPropertyModifiedTagSet = true; - m_CurrentImage = dynamic_cast(propNode->GetData()); + m_CurrentImage = dynamic_cast(propNode->GetData()); - m_SettingImgForLvlWinProp = true; + m_LevelWindowMutex = true; propNode->SetBoolProperty("imageForLevelWindow", true); - m_SettingImgForLvlWinProp = false; + m_LevelWindowMutex = false; + + this->Modified(); +} + +void mitk::LevelWindowManager::SetLevelWindow(const LevelWindow &levelWindow) +{ + if (m_LevelWindowProperty.IsNotNull()) + { + m_LevelWindowProperty->SetLevelWindow(levelWindow); + for (const auto &dataNode : m_RelevantDataNodes) + { + auto levelWindowProperty = dynamic_cast(dataNode->GetProperty("levelwindow")); + if (nullptr == levelWindowProperty) + { + continue; + } + levelWindowProperty->SetLevelWindow(levelWindow); + } + } this->Modified(); } -// returns the current mitkLevelWindowProperty object from the image that is affected by changes mitk::LevelWindowProperty::Pointer mitk::LevelWindowManager::GetLevelWindowProperty() { return m_LevelWindowProperty; } -// returns Level/Window values for the current image const mitk::LevelWindow &mitk::LevelWindowManager::GetLevelWindow() { if (m_LevelWindowProperty.IsNotNull()) { return m_LevelWindowProperty->GetLevelWindow(); } else { - itkExceptionMacro("No LevelWindow available!"); + mitkThrow() << "No LevelWindow available!"; } } -// sets new Level/Window values and informs all listeners about changes -void mitk::LevelWindowManager::SetLevelWindow(const mitk::LevelWindow &levelWindow) +bool mitk::LevelWindowManager::IsAutoTopMost() { - if (m_LevelWindowProperty.IsNotNull()) - { - m_LevelWindowProperty->SetLevelWindow(levelWindow); - } - this->Modified(); + return m_AutoTopMost; } -void mitk::LevelWindowManager::DataStorageAddedNode(const mitk::DataNode *) +bool mitk::LevelWindowManager::IsSelectedImages() +{ + return m_SelectedImagesMode; +} + +void mitk::LevelWindowManager::DataStorageAddedNode(const DataNode *) { // update observers with new data storage - UpdateObservers(); + this->UpdateObservers(); // Initialize LevelWindowsManager to new image - SetAutoTopMostImage(true); + this->SetAutoTopMostImage(true); // check if everything is still ok - if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || - (m_PropObserverToNode2.size() != this->GetRelevantNodes()->size())) + if ((m_ObserverToVisibleProperty.size() != m_ObserverToLayerProperty.size()) || + (m_ObserverToLayerProperty.size() != this->GetRelevantNodes()->size())) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } -void mitk::LevelWindowManager::DataStorageRemovedNode(const mitk::DataNode *removedNode) +void mitk::LevelWindowManager::DataStorageRemovedNode(const DataNode *removedNode) { - // first: check if deleted node is part of relevant nodes. If not, abort method because there is no need change - // anything. - if ((this->GetRelevantNodes()->size() == 0)) - return; + // First: check if deleted node is part of relevant nodes. + // If not, abort method because there is no need change anything. bool removedNodeIsRelevant = false; - /* Iterator code: is crashing, don't know why... so using for loop - for (mitk::DataStorage::SetOfObjects::ConstIterator it = this->GetRelevantNodes()->Begin(); - it != this->GetRelevantNodes()->End(); - ++it) - {if (it->Value() == removedNode) {removedNodeIsRelevant=true;}}*/ - for (unsigned int i = 0; i < this->GetRelevantNodes()->size(); i++) - { - if (this->GetRelevantNodes()->at(i) == removedNode) + DataStorage::SetOfObjects::ConstPointer relevantNodes = GetRelevantNodes(); + for (DataStorage::SetOfObjects::ConstIterator it = relevantNodes->Begin(); it != relevantNodes->End(); ++it) + { + if (it->Value() == removedNode) { removedNodeIsRelevant = true; } } - if (!removedNodeIsRelevant) + + if (false == removedNodeIsRelevant) + { return; + } // remember node which will be removed m_NodeMarkedToDelete = removedNode; // update observers - UpdateObservers(); + this->UpdateObservers(); - /* search image than belongs to the property */ + // search image that belongs to the property if (m_LevelWindowProperty.IsNull()) { - SetAutoTopMostImage(true, removedNode); + this->SetAutoTopMostImage(true, removedNode); } else { - mitk::NodePredicateProperty::Pointer p2 = mitk::NodePredicateProperty::New("levelwindow", m_LevelWindowProperty); - mitk::DataNode *n = m_DataStorage->GetNode(p2); - if (n == nullptr || m_AutoTopMost) // if node was deleted, change our behaviour to AutoTopMost, if AutoTopMost is true - // change level window to topmost node + NodePredicateProperty::Pointer property = NodePredicateProperty::New("levelwindow", m_LevelWindowProperty); + DataNode *n = m_DataStorage->GetNode(property); + if (n == nullptr || m_AutoTopMost) // if node was deleted, change our behavior to AutoTopMost, if AutoTopMost is + // true change level window to topmost node { - SetAutoTopMostImage(true, removedNode); + this->SetAutoTopMostImage(true, removedNode); } } // reset variable m_NodeMarkedToDelete = nullptr; // check if everything is still ok - if ((m_PropObserverToNode.size() != m_PropObserverToNode2.size()) || - (m_PropObserverToNode2.size() != (this->GetRelevantNodes()->size() - 1))) + if ((m_ObserverToVisibleProperty.size() != m_ObserverToLayerProperty.size()) || + (m_ObserverToLayerProperty.size() != (relevantNodes->size() - 1))) { mitkThrow() << "Wrong number of observers in Level Window Manager!"; } } -void mitk::LevelWindowManager::UpdateObservers() -{ - this->ClearPropObserverLists(); // remove old observers - CreatePropObserverLists(); // create new observer lists -} - -int mitk::LevelWindowManager::GetNumberOfObservers() -{ - return m_PropObserverToNode.size(); -} - -mitk::DataStorage *mitk::LevelWindowManager::GetDataStorage() +void mitk::LevelWindowManager::OnPropertyModified(const itk::EventObject &) { - return m_DataStorage.GetPointer(); + this->Modified(); } -// true if changes on slider or line-edits will affect always the topmost layer image -bool mitk::LevelWindowManager::isAutoTopMost() +mitk::Image *mitk::LevelWindowManager::GetCurrentImage() { - return m_AutoTopMost; + return m_CurrentImage; } -void mitk::LevelWindowManager::RecaluclateLevelWindowForSelectedComponent(const itk::EventObject &event) +int mitk::LevelWindowManager::GetNumberOfObservers() { - mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); - for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) - { - mitk::DataNode::Pointer node = it->Value(); - if (node.IsNull()) - continue; - - bool isSelected = false; - node->GetBoolProperty("selected", isSelected); - if (isSelected) - { - mitk::LevelWindow selectedLevelWindow; - node->GetLevelWindow(selectedLevelWindow); // node is an image node because of predicates - - auto *image = dynamic_cast(node->GetData()); - int displayedComponent = 0; - if (image && (node->GetIntProperty("Image.Displayed Component", displayedComponent))) - { // we found a selected image with a displayed component - // let's recalculate the levelwindow for this. - selectedLevelWindow.SetAuto(image, true, true, static_cast(displayedComponent)); - node->SetLevelWindow(selectedLevelWindow); - } - } - - mitk::LevelWindow levelWindow; - node->GetLevelWindow(levelWindow); - } - - this->Update(event); + return m_ObserverToVisibleProperty.size(); } -void mitk::LevelWindowManager::Update(const itk::EventObject &) // visible property of a image has changed +mitk::DataStorage::SetOfObjects::ConstPointer mitk::LevelWindowManager::GetRelevantNodes() { - if (m_SettingImgForLvlWinProp) // no mutex, should still help - { - return; - } - - if (m_AutoTopMost) - { - SetAutoTopMostImage(true); - return; - } - - int maxVisibleLayer = itk::NumericTraits::min(); - mitk::DataNode::Pointer highestVisible = nullptr; - std::vector visProbNodes; - - mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); - for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) + if (m_DataStorage.IsNull()) { - mitk::DataNode::Pointer node = it->Value(); - - if (node.IsNull()) - { - continue; - } - - bool visible = node->IsVisible(nullptr); - - if (node->IsVisible(nullptr)) - { - int layer = -1; - node->GetIntProperty("layer", layer); - if (layer > maxVisibleLayer) - { - maxVisibleLayer = layer; - highestVisible = node; - } - } - - bool prop = false; - node->GetBoolProperty("imageForLevelWindow", prop); - - if (prop && visible) - { - visProbNodes.push_back(node); - } + return DataStorage::SetOfObjects::ConstPointer(DataStorage::SetOfObjects::New()); } - int numVisProbNodes = visProbNodes.size(); - if (numVisProbNodes > 2) - { - MITK_ERROR << "Error: not more than two visible nodes are expected to have the imageForLevelWindow property set at " - "any point."; - } - else if (numVisProbNodes == 2) - { - for (std::vector::const_iterator it = visProbNodes.begin(); it != visProbNodes.end(); ++it) - { - mitk::LevelWindowProperty::Pointer newProp = - dynamic_cast((*it)->GetProperty("levelwindow")); - if (newProp != m_LevelWindowProperty) - { - this->SetLevelWindowProperty(newProp); - return; - } - } - } - else if (numVisProbNodes == 1) - { - mitk::LevelWindowProperty::Pointer newProp = - dynamic_cast(visProbNodes[0]->GetProperty("levelwindow")); - if (newProp != m_LevelWindowProperty) - { - this->SetLevelWindowProperty(newProp); - return; - } - } - else if (highestVisible) - { - mitk::LevelWindowProperty::Pointer lvlProp = - dynamic_cast(highestVisible->GetProperty("levelwindow")); + NodePredicateProperty::Pointer notBinary = NodePredicateProperty::New("binary", BoolProperty::New(false)); + NodePredicateProperty::Pointer hasLevelWindow = NodePredicateProperty::New("levelwindow", nullptr); - this->SetLevelWindowProperty(lvlProp); - } - else - { - Modified(); - } -} - -mitk::DataStorage::SetOfObjects::ConstPointer mitk::LevelWindowManager::GetRelevantNodes() -{ - if (m_DataStorage.IsNull()) - return mitk::DataStorage::SetOfObjects::ConstPointer(mitk::DataStorage::SetOfObjects::New()); // return empty set - - mitk::NodePredicateProperty::Pointer notBinary = - mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(false)); - mitk::NodePredicateProperty::Pointer hasLevelWindow = mitk::NodePredicateProperty::New("levelwindow", nullptr); - - mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); - mitk::NodePredicateDataType::Pointer isDImage = mitk::NodePredicateDataType::New("DiffusionImage"); - mitk::NodePredicateDataType::Pointer isTImage = mitk::NodePredicateDataType::New("TensorImage"); - mitk::NodePredicateDataType::Pointer isOdfImage = mitk::NodePredicateDataType::New("OdfImage"); - mitk::NodePredicateDataType::Pointer isShImage = mitk::NodePredicateDataType::New("ShImage"); - mitk::NodePredicateOr::Pointer predicateTypes = mitk::NodePredicateOr::New(); + NodePredicateDataType::Pointer isImage = NodePredicateDataType::New("Image"); + NodePredicateDataType::Pointer isDImage = NodePredicateDataType::New("DiffusionImage"); + NodePredicateDataType::Pointer isTImage = NodePredicateDataType::New("TensorImage"); + NodePredicateDataType::Pointer isOdfImage = NodePredicateDataType::New("OdfImage"); + NodePredicateDataType::Pointer isShImage = NodePredicateDataType::New("ShImage"); + NodePredicateOr::Pointer predicateTypes = NodePredicateOr::New(); predicateTypes->AddPredicate(isImage); predicateTypes->AddPredicate(isDImage); predicateTypes->AddPredicate(isTImage); predicateTypes->AddPredicate(isOdfImage); predicateTypes->AddPredicate(isShImage); - mitk::NodePredicateAnd::Pointer predicate = mitk::NodePredicateAnd::New(); + NodePredicateAnd::Pointer predicate = NodePredicateAnd::New(); predicate->AddPredicate(notBinary); predicate->AddPredicate(hasLevelWindow); predicate->AddPredicate(predicateTypes); - mitk::DataStorage::SetOfObjects::ConstPointer relevantNodes = m_DataStorage->GetSubset(predicate); + DataStorage::SetOfObjects::ConstPointer relevantNodes = m_DataStorage->GetSubset(predicate); return relevantNodes; } -mitk::Image *mitk::LevelWindowManager::GetCurrentImage() +void mitk::LevelWindowManager::UpdateObservers() { - return m_CurrentImage; + this->ClearPropObserverLists(); // remove old observers + this->CreatePropObserverLists(); // create new observer lists } void mitk::LevelWindowManager::ClearPropObserverLists() { - for (auto iter = m_PropObserverToNode.begin(); iter != m_PropObserverToNode.end(); ++iter) + for (auto iter = m_ObserverToVisibleProperty.begin(); iter != m_ObserverToVisibleProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } - m_PropObserverToNode.clear(); + m_ObserverToVisibleProperty.clear(); - for (auto iter = m_PropObserverToNode2.begin(); iter != m_PropObserverToNode2.end(); - ++iter) + for (auto iter = m_ObserverToLayerProperty.begin(); iter != m_ObserverToLayerProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } - m_PropObserverToNode2.clear(); + m_ObserverToLayerProperty.clear(); - for (auto iter = m_PropObserverToNode3.begin(); iter != m_PropObserverToNode3.end(); - ++iter) + for (auto iter = m_ObserverToRenderingModeProperty.begin(); iter != m_ObserverToRenderingModeProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } - m_PropObserverToNode3.clear(); + m_ObserverToRenderingModeProperty.clear(); - for (auto iter = m_PropObserverToNode4.begin(); iter != m_PropObserverToNode4.end(); - ++iter) + for (auto iter = m_ObserverToDisplayedComponentProperty.begin(); iter != m_ObserverToDisplayedComponentProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } - m_PropObserverToNode4.clear(); + m_ObserverToDisplayedComponentProperty.clear(); - for (auto iter = m_PropObserverToNode5.begin(); iter != m_PropObserverToNode5.end(); - ++iter) + for (auto iter = m_ObserverToLevelWindowImageProperty.begin(); iter != m_ObserverToLevelWindowImageProperty.end(); ++iter) { (*iter).second->RemoveObserver((*iter).first.first); (*iter).second = nullptr; } - m_PropObserverToNode5.clear(); + m_ObserverToLevelWindowImageProperty.clear(); + + for (auto iter = m_ObserverToSelectedProperty.begin(); iter != m_ObserverToSelectedProperty.end(); ++iter) + { + (*iter).second->RemoveObserver((*iter).first.first); + (*iter).second = nullptr; + } + m_ObserverToSelectedProperty.clear(); } void mitk::LevelWindowManager::CreatePropObserverLists() { if (m_DataStorage.IsNull()) // check if data storage is set { - itkExceptionMacro("DataStorage not set"); + mitkThrow() << "DataStorage not set"; } /* add observers for all relevant nodes */ - mitk::DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); - for (mitk::DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) + DataStorage::SetOfObjects::ConstPointer all = this->GetRelevantNodes(); + for (DataStorage::SetOfObjects::ConstIterator it = all->Begin(); it != all->End(); ++it) { if ((it->Value().IsNull()) || (it->Value() == m_NodeMarkedToDelete)) { continue; } /* register listener for changes in visible property */ itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long visIdx = it->Value()->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command); - m_PropObserverToNode[PropDataPair(visIdx, it->Value())] = it->Value()->GetProperty("visible"); + m_ObserverToVisibleProperty[PropDataPair(visIdx, it->Value())] = it->Value()->GetProperty("visible"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction(this, &LevelWindowManager::Update); unsigned long layerIdx = it->Value()->GetProperty("layer")->AddObserver(itk::ModifiedEvent(), command2); - m_PropObserverToNode2[PropDataPair(layerIdx, it->Value())] = it->Value()->GetProperty("layer"); + m_ObserverToLayerProperty[PropDataPair(layerIdx, it->Value())] = it->Value()->GetProperty("layer"); /* register listener for changes in layer property */ itk::ReceptorMemberCommand::Pointer command3 = itk::ReceptorMemberCommand::New(); command3->SetCallbackFunction(this, &LevelWindowManager::Update); - mitk::BaseProperty::Pointer imageRenderingMode = it->Value()->GetProperty("Image Rendering.Mode"); + BaseProperty::Pointer imageRenderingMode = it->Value()->GetProperty("Image Rendering.Mode"); if (imageRenderingMode.IsNotNull()) { unsigned long rendIdx = imageRenderingMode->AddObserver(itk::ModifiedEvent(), command3); - m_PropObserverToNode3[PropDataPair(rendIdx, it->Value())] = imageRenderingMode.GetPointer(); + m_ObserverToRenderingModeProperty[PropDataPair(rendIdx, it->Value())] = imageRenderingMode.GetPointer(); } itk::ReceptorMemberCommand::Pointer command4 = itk::ReceptorMemberCommand::New(); - command4->SetCallbackFunction(this, &LevelWindowManager::RecaluclateLevelWindowForSelectedComponent); - mitk::BaseProperty::Pointer displayedImageComponent = it->Value()->GetProperty("Image.Displayed Component"); + command4->SetCallbackFunction(this, &LevelWindowManager::RecalculateLevelWindowForSelectedComponent); + BaseProperty::Pointer displayedImageComponent = it->Value()->GetProperty("Image.Displayed Component"); if (displayedImageComponent.IsNotNull()) { unsigned long dispIdx = displayedImageComponent->AddObserver(itk::ModifiedEvent(), command4); - m_PropObserverToNode4[PropDataPair(dispIdx, it->Value())] = displayedImageComponent.GetPointer(); + m_ObserverToDisplayedComponentProperty[PropDataPair(dispIdx, it->Value())] = displayedImageComponent.GetPointer(); } itk::ReceptorMemberCommand::Pointer command5 = itk::ReceptorMemberCommand::New(); command5->SetCallbackFunction(this, &LevelWindowManager::Update); - mitk::BaseProperty::Pointer imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); + BaseProperty::Pointer imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); if (imgForLvlWin.IsNull()) { it->Value()->SetBoolProperty("imageForLevelWindow", false); imgForLvlWin = it->Value()->GetProperty("imageForLevelWindow"); } unsigned long lvlWinIdx = imgForLvlWin->AddObserver(itk::ModifiedEvent(), command5); - m_PropObserverToNode5[PropDataPair(lvlWinIdx, it->Value())] = it->Value()->GetProperty("imageForLevelWindow"); + m_ObserverToLevelWindowImageProperty[PropDataPair(lvlWinIdx, it->Value())] = it->Value()->GetProperty("imageForLevelWindow"); + + itk::ReceptorMemberCommand::Pointer command6 = + itk::ReceptorMemberCommand::New(); + command6->SetCallbackFunction(this, &LevelWindowManager::UpdateSelected); + BaseProperty::Pointer selectedDataNode = it->Value()->GetProperty("selected"); + if (selectedDataNode.IsNull()) + { + it->Value()->SetBoolProperty("selected", false); + selectedDataNode = it->Value()->GetProperty("selected"); + } + unsigned long selectedIdx = selectedDataNode->AddObserver(itk::ModifiedEvent(), command5); + m_ObserverToSelectedProperty[PropDataPair(selectedIdx, it->Value())] = it->Value()->GetProperty("selected"); + } +} + +bool mitk::LevelWindowManager::IgnoreNode(const DataNode* dataNode) +{ + LevelWindowProperty::Pointer levelWindowProperty = + dynamic_cast(dataNode->GetProperty("levelwindow")); + if (levelWindowProperty.IsNull()) + { + return true; } + + int nonLvlWinMode1 = RenderingModeProperty::LOOKUPTABLE_COLOR; + int nonLvlWinMode2 = RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR; + + RenderingModeProperty::Pointer mode = + dynamic_cast(dataNode->GetProperty("Image Rendering.Mode")); + + if (mode.IsNotNull()) + { + int currMode = mode->GetRenderingMode(); + if (currMode == nonLvlWinMode1 || currMode == nonLvlWinMode2) + { + return true; + } + } + else + { + return true; + } + + return false; } diff --git a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp index b0868e59b5..52c3abf645 100644 --- a/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkPlaneGeometry.cpp @@ -1,984 +1,975 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #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) { } - void PlaneGeometry::EnsurePerpendicularNormal(mitk::AffineTransform3D *transform) + bool PlaneGeometry::CheckRotationMatrix(mitk::AffineTransform3D *transform, double epsilon) { - /** \brief ensure column(2) of indexToWorldTransform-matrix to be perpendicular to plane, keep length and - * handedness. - */ + bool rotation = true; - VnlVector normal = vnl_cross_3d(transform->GetMatrix().GetVnlMatrix().get_column(0), - transform->GetMatrix().GetVnlMatrix().get_column(1)); - normal.normalize(); // Now normal is a righthand normal unit vector, perpendicular to the plane. + auto matrix = transform->GetMatrix().GetVnlMatrix(); + matrix.normalize_columns(); - ScalarType len = transform->GetMatrix().GetVnlMatrix().get_column(2).two_norm(); - if (len == 0) + auto det = vnl_determinant(matrix); + if (fabs(det-1.0) > epsilon) { - len = 1; + MITK_WARN << "Invalid rotation matrix! Determinant != 1 (" << det << ")"; + rotation = false; } - normal *= len; - // Get the existing normal vector zed: - vnl_vector_fixed zed = transform->GetMatrix().GetVnlMatrix().get_column(2); - - /** If det(matrix)<0, multiply normal vector by (-1) to keep geometry lefthanded. */ - if (vnl_determinant(transform->GetMatrix().GetVnlMatrix()) < 0) + 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_DEBUG << "EnsurePerpendicularNormal(): Lefthanded geometry preserved, rh-normal: [ " << normal << " ],"; - normal *= (-1.0); - MITK_DEBUG << "lh-normal: [ " << normal << " ], original vector zed is: [ " << zed << " ]"; + MITK_WARN << "Invalid rotation matrix! R*R^T != ID. Max value: " << max << " (should be 0)"; + rotation = false; } - // Now lets compare and only replace if necessary and only then warn the user: - - // float epsilon is precise enough here, but we need to respect numerical condition: - // Higham, N., 2002, Accuracy and Stability of Numerical Algorithms, - // SIAM, page 37, 2nd edition: - double feps = std::numeric_limits::epsilon(); - double zedsMagnitude = zed.two_norm(); - feps = feps * zedsMagnitude * 2; - - /** Check if normal (3. column) was perpendicular: If not, replace with calculated normal vector: */ - if (normal != zed) - { - vnl_vector_fixed parallel; - for (unsigned int i = 0; i < 3; ++i) - { - parallel[i] = normal[i] / zed[i]; // Remember linear algebra: checking for parallelity. - } - // Checking if really not paralell i.e. non-colinear vectors by comparing these floating point numbers: - if ((parallel[0] + feps < parallel[1] || feps + parallel[1] < parallel[0]) && - (parallel[0] + feps < parallel[2] || feps + parallel[2] < parallel[0])) - { - MITK_WARN - << "EnsurePerpendicularNormal(): Plane geometry was _/askew/_, so here it gets rectified by substituting" - << " the 3rd column of the indexToWorldMatrix with an appropriate normal vector: [ " << normal - << " ], original vector zed was: [ " << zed << " ]."; - - Matrix3D matrix = transform->GetMatrix(); - matrix.GetVnlMatrix().set_column(2, normal); - transform->SetMatrix(matrix); - } - } - else - { - // Nothing to do, 3rd column of indexToWorldTransformMatrix already was perfectly perpendicular. - } + return rotation; } void PlaneGeometry::CheckIndexToWorldTransform(mitk::AffineTransform3D *transform) { - EnsurePerpendicularNormal(transform); + 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 ); } 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); 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.GetInverse(); + mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetTranspose(); /// The index of the sagittal, coronal and axial axes in the reference geometry. - int axes[3]; + 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) + for (int i=0; i<3; ++i) { - int dominantAxis = itk::Function::Max3( - inverseMatrix[0][i], - inverseMatrix[1][i], - inverseMatrix[2][i] - ); - axes[i] = dominantAxis; + 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 = itk::Function::Max3( - inverseMatrix[0][worldAxis], - inverseMatrix[1][worldAxis], - inverseMatrix[2][worldAxis]); + 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)); return frontToBack; } VnlVector PlaneGeometry::GetNormalVnl() const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2); } 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 { 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(); 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/mitkPropertyRelationRuleBase.cpp b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp index a3674cb0a1..7cb8e6b969 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp @@ -1,772 +1,762 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPropertyRelationRuleBase.h" #include #include #include #include #include #include #include bool mitk::PropertyRelationRuleBase::IsAbstract() const { return true; }; bool mitk::PropertyRelationRuleBase::IsSourceCandidate(const IPropertyProvider *owner) const { return owner != nullptr; }; bool mitk::PropertyRelationRuleBase::IsDestinationCandidate(const IPropertyProvider *owner) const { return owner != nullptr; }; mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRootKeyPath() { return PropertyKeyPath().AddElement("MITK").AddElement("Relations"); }; bool mitk::PropertyRelationRuleBase::IsSupportedRuleID(const RuleIDType& ruleID) const { return ruleID == this->GetRuleID(); }; std::string mitk::PropertyRelationRuleBase::GetRIIPropertyRegEx(const std::string propName, const InstanceIDType &instanceID) const { auto path = this->GetRootKeyPath(); if (instanceID.empty()) { path.AddAnyElement(); } else { path.AddElement(instanceID); } if (!propName.empty()) { path.AddElement(propName); } return PropertyKeyPathToPropertyRegEx(path); }; //workaround until T24729 is done. Please remove if T24728 is done //then could directly use owner->GetPropertyKeys() again. -auto GetPropertyKeys(const mitk::IPropertyProvider *owner) +std::vector mitk::PropertyRelationRuleBase::GetPropertyKeys(const mitk::IPropertyProvider *owner) { std::vector keys; auto sourceCasted = dynamic_cast(owner); if (sourceCasted) { auto sourceData = sourceCasted->GetData(); if (sourceData) { keys = sourceData->GetPropertyKeys(); } else { keys = sourceCasted->GetPropertyKeys(); } } else { keys = owner->GetPropertyKeys(); } return keys; }; //end workaround for T24729 bool mitk::PropertyRelationRuleBase::IsSource(const IPropertyProvider *owner) const { if (!owner) { mitkThrow() << "Error. Passed owner pointer is NULL"; } std::vector keys; //workaround until T24729 is done. Please remove if T24728 is done keys = GetPropertyKeys(owner); //end workaround for T24729 auto sourceRegExStr = this->GetRIIPropertyRegEx("ruleID"); auto regEx = std::regex(sourceRegExStr); for (const auto &key : keys) { if (std::regex_match(key, regEx)) { auto idProp = owner->GetConstProperty(key); auto ruleID = idProp->GetValueAsString(); if (this->IsSupportedRuleID(ruleID)) { return true; } } } return false; }; mitk::PropertyRelationRuleBase::RelationType mitk::PropertyRelationRuleBase::HasRelation( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed owner pointer is NULL"; } RelationType result = RelationType::None; if (!this->GetInstanceID_IDLayer(source, destination).empty()) { // has relations of type Connected_ID; result = RelationType::Connected_ID; } if (result == RelationType::None) { auto instanceIDs_data = this->GetInstanceID_datalayer(source, destination); if (instanceIDs_data.size() > 1) { MITK_WARN << "Property relation on data level is ambiguous. First relation is used. Instance ID: " << instanceIDs_data.front(); } if (!instanceIDs_data.empty() && instanceIDs_data.front() != NULL_INSTANCE_ID()) { // has relations of type Connected_Data; result = RelationType::Connected_Data; } } if (result == RelationType::None) { if (this->HasImplicitDataRelation(source, destination)) { // has relations of type Connected_Data; result = RelationType::Implicit_Data; } } return result; }; mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetExistingRelations( const IPropertyProvider *source) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } auto ruleIDRegExStr = this->GetRIIPropertyRegEx("ruleID"); auto regEx = std::regex(ruleIDRegExStr); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 RelationUIDVectorType relationUIDs; for (const auto &key : keys) { if (std::regex_match(key, regEx)) { auto idProp = source->GetConstProperty(key); auto ruleID = idProp->GetValueAsString(); if (this->IsSupportedRuleID(ruleID)) { auto instanceID = this->GetInstanceIDByPropertyName(key); relationUIDs.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); } } } return relationUIDs; }; mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetRelationUIDs( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } RelationUIDVectorType result; auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); for (const auto instanceID : instanceIDs) { result.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); } if (result.empty() || this->IsAbstract()) { auto instanceIDs_data = this->GetInstanceID_datalayer(source, destination); for (const auto instanceID : instanceIDs_data) { if (std::find(std::begin(instanceIDs), std::end(instanceIDs), instanceID) == std::end(instanceIDs)) { result.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); } } } return result; }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const { auto result = this->GetRelationUIDs(source, destination); if (result.empty()) { mitkThrowException(NoPropertyRelationException); } else if(result.size()>1) { mitkThrow() << "Cannot return one(!) relation UID. Multiple relations exists for given rule, source and destination."; } return result[0]; }; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::NULL_INSTANCE_ID() { return std::string(); }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUIDByInstanceID( const IPropertyProvider *source, const InstanceIDType &instanceID) const { RelationUIDType result; if (instanceID != NULL_INSTANCE_ID()) { auto idProp = source->GetConstProperty( PropertyKeyPathToPropertyName(this->GetRootKeyPath().AddElement(instanceID).AddElement("relationUID"))); if (idProp.IsNotNull()) { result = idProp->GetValueAsString(); } } if (result.empty()) { mitkThrowException(NoPropertyRelationException); } return result; }; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByRelationUID( const IPropertyProvider *source, const RelationUIDType &relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } InstanceIDType result = NULL_INSTANCE_ID(); auto destRegExStr = PropertyKeyPathToPropertyRegEx(GetRootKeyPath().AddAnyElement().AddElement("relationUID")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == relationUID) { if (instance_matches.size()>1) { result = instance_matches[1]; break; } } } } return result; }; mitk::PropertyRelationRuleBase::InstanceIDVectorType mitk::PropertyRelationRuleBase::GetInstanceID_IDLayer( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } auto identifiable = dynamic_cast(destination); if (!identifiable) { //This check and pass through to data is needed due to solve T25711. See Task for more information. //This could be removed at the point we can get rid of DataNodes or they get realy transparent. auto node = dynamic_cast(destination); if (node && node->GetData()) { identifiable = dynamic_cast(node->GetData()); } } InstanceIDVectorType result; if (identifiable) { // check for relations of type Connected_ID; auto destRegExStr = this->GetRIIPropertyRegEx("destinationUID"); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; auto destUID = identifiable->GetUID(); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == destUID) { if (instance_matches.size()>1) { auto instanceID = instance_matches[1]; if (this->IsSupportedRuleID(GetRuleIDByInstanceID(source, instanceID))) { result.push_back(instanceID); } } } } } } return result; }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::Connect(IPropertyOwner *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } if (this->IsAbstract()) { mitkThrow() << "Error. This is an abstract property relation rule. Abstract rule must not make a connection. Please use a concrete rule."; } auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); bool hasIDlayer = !instanceIDs.empty(); auto instanceIDs_data = this->GetInstanceID_datalayer(source, destination); if (instanceIDs_data.size() > 1) { MITK_WARN << "Property relation on data level is ambiguous. First relation is used. Instance ID: " << instanceIDs_data.front(); } bool hasDatalayer = !instanceIDs_data.empty(); if (hasIDlayer && hasDatalayer && instanceIDs.front() != instanceIDs_data.front()) { mitkThrow() << "Property relation information is in an invalid state. ID and data layer point to different " "relation instances. Rule: " << this->GetRuleID() << "; ID based instance: " << instanceIDs.front() << "; Data base instance: " << instanceIDs_data.front(); } RelationUIDType relationUID = this->CreateRelationUID(); InstanceIDType instanceID = ""; if (hasIDlayer) { instanceID = instanceIDs.front(); } else if (hasDatalayer) { instanceID = instanceIDs_data.front(); } else { instanceID = this->CreateNewRelationInstance(source, relationUID); } auto relUIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("relationUID")); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); auto ruleIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("ruleID")); source->SetProperty(ruleIDKey, mitk::StringProperty::New(this->GetRuleID())); if (!hasIDlayer) { auto identifiable = dynamic_cast(destination); if (!identifiable) { //This check and pass through to data is needed due to solve T25711. See Task for more information. //This could be removed at the point we can get rid of DataNodes or they get realy transparent. auto node = dynamic_cast(destination); if (node && node->GetData()) { identifiable = dynamic_cast(node->GetData()); } } if (identifiable) { auto destUIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("destinationUID")); source->SetProperty(destUIDKey, mitk::StringProperty::New(identifiable->GetUID())); } } if (!hasDatalayer) { this->Connect_datalayer(source, destination, instanceID); } return relationUID; }; void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, const IPropertyProvider *destination) const { try { const auto relationUIDs = this->GetRelationUIDs(source, destination); for (const auto relUID: relationUIDs) { this->Disconnect(source, relUID); } } catch (const NoPropertyRelationException &) { // nothing to do and no real error in context of disconnect. } }; void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, RelationUIDType relationUID) const { auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); if (instanceID != NULL_INSTANCE_ID()) { this->Disconnect_datalayer(source, instanceID); auto instancePrefix = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID)); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (key.find(instancePrefix) == 0) { source->RemoveProperty(key); } } } }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::CreateRelationUID() { UIDGenerator generator; return generator.GetUID(); }; /**This mutex is used to guard mitk::PropertyRelationRuleBase::CreateNewRelationInstance by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently. It is not in the class interface itself, because it is an implementation detail. */ std::mutex relationCreationLock; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::CreateNewRelationInstance( IPropertyOwner *source, const RelationUIDType &relationUID) const { std::lock_guard guard(relationCreationLock); ////////////////////////////////////// // Get all existing instanc IDs std::vector instanceIDs; InstanceIDType newID = "1"; auto destRegExStr = PropertyKeyPathToPropertyRegEx(this->GetRootKeyPath().AddAnyElement().AddElement("relationUID")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { if (instance_matches.size()>1) { instanceIDs.push_back(std::stoi(instance_matches[1])); } } } ////////////////////////////////////// // Get new ID std::sort(instanceIDs.begin(), instanceIDs.end()); if (!instanceIDs.empty()) { newID = std::to_string(instanceIDs.back() + 1); } ////////////////////////////////////// // reserve new ID auto relUIDKey = PropertyKeyPathToPropertyName(this->GetRootKeyPath().AddElement(newID).AddElement("relationUID")); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); return newID; }; itk::LightObject::Pointer mitk::PropertyRelationRuleBase::InternalClone() const { return Superclass::InternalClone(); }; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByPropertyName(const std::string propName) { auto proppath = PropertyNameToPropertyKeyPath(propName); auto ref = GetRootKeyPath(); if (proppath.GetSize() < 3 || !(proppath.GetFirstNode() == ref.GetFirstNode()) || !(proppath.GetNode(1) == ref.GetNode(1))) { mitkThrow() << "Property name is not for a RII property or containes no instance ID. Wrong name: " << propName; } return proppath.GetNode(2).name; }; mitk::PropertyRelationRuleBase::RuleIDType mitk::PropertyRelationRuleBase::GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const { if (!source) { mitkThrow() << "Error. Source is invalid. Cannot deduce rule ID"; } - auto regExStr = this->GetRIIPropertyRegEx("ruleID", instanceID); + auto path = GetRootKeyPath().AddElement(instanceID).AddElement("ruleID"); + auto name = PropertyKeyPathToPropertyName(path); - auto regEx = std::regex(regExStr); - - //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. - const auto keys = GetPropertyKeys(source); - //end workaround for T24729 - - RelationUIDVectorType relationUIDs; + const auto prop = source->GetConstProperty(name); std::string result; - for (const auto &key : keys) - { - if (std::regex_match(key, regEx)) - { - auto idProp = source->GetConstProperty(key); - result = idProp->GetValueAsString(); - break; - } - } + if (prop.IsNotNull()) + { + result = prop->GetValueAsString(); + } if (result.empty()) { mitkThrowException(NoPropertyRelationException) << "Error. Source has no property relation with the passed instance ID. Instance ID: " << instanceID; } return result; }; namespace mitk { /** * \brief Predicate used to wrap rule checks. * * \ingroup DataStorage */ class NodePredicateRuleFunction : public NodePredicateBase { public: using FunctionType = std::function; mitkClassMacro(NodePredicateRuleFunction, NodePredicateBase) mitkNewMacro2Param(NodePredicateRuleFunction, const FunctionType &, PropertyRelationRuleBase::ConstPointer) ~NodePredicateRuleFunction() override = default; bool CheckNode(const mitk::DataNode *node) const override { if (!node) { return false; } return m_Function(node, m_Rule); }; protected: explicit NodePredicateRuleFunction(const FunctionType &function, PropertyRelationRuleBase::ConstPointer rule) : m_Function(function), m_Rule(rule) { }; FunctionType m_Function; PropertyRelationRuleBase::ConstPointer m_Rule; }; } // namespace mitk mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourceCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSourceCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsDestinationCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetConnectedSourcesDetector() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSource(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourcesDetector( const IPropertyProvider *destination, RelationType minimalRelation) const { if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } auto check = [destination, minimalRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->HasRelation(node, destination) >= minimalRelation; }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationsDetector( const IPropertyProvider *source, RelationType minimalRelation) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } auto check = [source, minimalRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->HasRelation(source, node) >= minimalRelation; }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationDetector( const IPropertyProvider *source, RelationUIDType relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } auto relUIDs = this->GetExistingRelations(source); if (std::find(relUIDs.begin(), relUIDs.end(), relationUID) == relUIDs.end()) { mitkThrow() << "Error. Passed relationUID does not identify a relation instance of the passed source for this rule instance."; }; auto check = [source, relationUID](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { try { auto relevantUIDs = rule->GetRelationUIDs(source, node); for (const auto& aUID : relevantUIDs) { if (aUID == relationUID) { return true; } } } catch(const NoPropertyRelationException &) { return false; } return false; }; return NodePredicateRuleFunction::New(check, this).GetPointer(); }; diff --git a/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp b/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp index 9a3a8e1e4e..4e4cd940a1 100644 --- a/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp +++ b/Modules/Core/src/DataManagement/mitkSlicedGeometry3D.cpp @@ -1,970 +1,966 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include "mitkSlicedGeometry3D.h" #include "mitkAbstractTransformGeometry.h" #include "mitkApplyTransformMatrixOperation.h" #include "mitkInteractionConst.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkRotationOperation.h" #include "mitkSliceNavigationController.h" const mitk::ScalarType PI = 3.14159265359; mitk::SlicedGeometry3D::SlicedGeometry3D() : m_EvenlySpaced(true), m_Slices(0), m_ReferenceGeometry(nullptr), m_SliceNavigationController(nullptr) { m_DirectionVector.Fill(0); this->InitializeSlicedGeometry(m_Slices); } mitk::SlicedGeometry3D::SlicedGeometry3D(const SlicedGeometry3D &other) : Superclass(other), m_EvenlySpaced(other.m_EvenlySpaced), m_Slices(other.m_Slices), m_ReferenceGeometry(other.m_ReferenceGeometry), m_SliceNavigationController(other.m_SliceNavigationController) { m_DirectionVector.Fill(0); SetSpacing(other.GetSpacing()); SetDirectionVector(other.GetDirectionVector()); if (m_EvenlySpaced) { assert(!other.m_PlaneGeometries.empty() && "This may happen when you use one of the old Initialize methods, which had a bool parameter that is implicitly casted to the number of slices now."); PlaneGeometry::Pointer geometry = other.m_PlaneGeometries[0]->Clone(); assert(geometry.IsNotNull()); SetPlaneGeometry(geometry, 0); } else { unsigned int s; for (s = 0; s < other.m_Slices; ++s) { if (other.m_PlaneGeometries[s].IsNull()) { assert(other.m_EvenlySpaced); m_PlaneGeometries[s] = nullptr; } else { PlaneGeometry *geometry2D = other.m_PlaneGeometries[s]->Clone(); assert(geometry2D != nullptr); SetPlaneGeometry(geometry2D, s); } } } } mitk::SlicedGeometry3D::~SlicedGeometry3D() { } mitk::PlaneGeometry *mitk::SlicedGeometry3D::GetPlaneGeometry(int s) const { mitk::PlaneGeometry::Pointer geometry2D = nullptr; if (this->IsValidSlice(s)) { geometry2D = m_PlaneGeometries[s]; // If (a) m_EvenlySpaced==true, (b) we don't have a PlaneGeometry stored // for the requested slice, and (c) the first slice (s=0) // is a PlaneGeometry instance, then we calculate the geometry of the // requested as the plane of the first slice shifted by m_Spacing[2]*s // in the direction of m_DirectionVector. if ((m_EvenlySpaced) && (geometry2D.IsNull())) { PlaneGeometry *firstSlice = m_PlaneGeometries[0]; if (firstSlice != nullptr && dynamic_cast(m_PlaneGeometries[0].GetPointer()) == nullptr) { if ((m_DirectionVector[0] == 0.0) && (m_DirectionVector[1] == 0.0) && (m_DirectionVector[2] == 0.0)) { m_DirectionVector = firstSlice->GetNormal(); m_DirectionVector.Normalize(); } Vector3D direction; direction = m_DirectionVector * this->GetSpacing()[2]; mitk::PlaneGeometry::Pointer requestedslice; requestedslice = static_cast(firstSlice->Clone().GetPointer()); requestedslice->SetOrigin(requestedslice->GetOrigin() + direction * s); geometry2D = requestedslice; m_PlaneGeometries[s] = geometry2D; } } return geometry2D; } else { return nullptr; } } const mitk::BoundingBox *mitk::SlicedGeometry3D::GetBoundingBox() const { assert(this->IsBoundingBoxNull() == false); return Superclass::GetBoundingBox(); } bool mitk::SlicedGeometry3D::SetPlaneGeometry(mitk::PlaneGeometry *geometry2D, int s) { if (this->IsValidSlice(s)) { m_PlaneGeometries[s] = geometry2D; m_PlaneGeometries[s]->SetReferenceGeometry(m_ReferenceGeometry); return true; } return false; } void mitk::SlicedGeometry3D::InitializeSlicedGeometry(unsigned int slices) { Superclass::Initialize(); m_Slices = slices; PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign(m_Slices, gnull); Vector3D spacing; spacing.Fill(1.0); this->SetSpacing(spacing); m_DirectionVector.Fill(0); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, unsigned int slices) { assert(geometry2D != nullptr); this->InitializeEvenlySpaced(geometry2D, geometry2D->GetExtentInMM(2) / geometry2D->GetExtent(2), slices); } void mitk::SlicedGeometry3D::InitializeEvenlySpaced(mitk::PlaneGeometry *geometry2D, mitk::ScalarType zSpacing, unsigned int slices) { assert(geometry2D != nullptr); assert(geometry2D->GetExtent(0) > 0); assert(geometry2D->GetExtent(1) > 0); geometry2D->Register(); Superclass::Initialize(); m_Slices = slices; BoundingBox::BoundsArrayType bounds = geometry2D->GetBounds(); bounds[4] = 0; bounds[5] = slices; // clear and reserve PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign(m_Slices, gnull); Vector3D directionVector = geometry2D->GetAxisVector(2); directionVector.Normalize(); directionVector *= zSpacing; // Normally we should use the following four lines to create a copy of // the transform contrained in geometry2D, because it may not be changed // by us. But we know that SetSpacing creates a new transform without // changing the old (coming from geometry2D), so we can use the fifth // line instead. We check this at (**). // // AffineTransform3D::Pointer transform = AffineTransform3D::New(); // transform->SetMatrix(geometry2D->GetIndexToWorldTransform()->GetMatrix()); // transform->SetOffset(geometry2D->GetIndexToWorldTransform()->GetOffset()); // SetIndexToWorldTransform(transform); this->SetIndexToWorldTransform(geometry2D->GetIndexToWorldTransform()); mitk::Vector3D spacing; FillVector3D(spacing, geometry2D->GetExtentInMM(0) / bounds[1], geometry2D->GetExtentInMM(1) / bounds[3], zSpacing); this->SetDirectionVector(directionVector); this->SetBounds(bounds); this->SetPlaneGeometry(geometry2D, 0); this->SetSpacing(spacing, true); this->SetEvenlySpaced(); // this->SetTimeBounds( geometry2D->GetTimeBounds() ); assert(this->GetIndexToWorldTransform() != geometry2D->GetIndexToWorldTransform()); // (**) see above. this->SetFrameOfReferenceID(geometry2D->GetFrameOfReferenceID()); this->SetImageGeometry(geometry2D->GetImageGeometry()); geometry2D->UnRegister(); } void mitk::SlicedGeometry3D::InitializePlanes(const mitk::BaseGeometry *geometry3D, mitk::PlaneGeometry::PlaneOrientation planeorientation, bool top, bool frontside, bool rotated) { m_ReferenceGeometry = geometry3D; PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(geometry3D, top, planeorientation, frontside, rotated); int worldAxis = planeorientation == PlaneGeometry::Sagittal ? 0 : planeorientation == PlaneGeometry::Frontal ? 1 : 2; // 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.GetInverse(); - - int dominantAxis = itk::Function::Max3( - inverseMatrix[0][worldAxis], - inverseMatrix[1][worldAxis], - inverseMatrix[2][worldAxis]); + mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetTranspose(); + int dominantAxis = planeGeometry->CalculateDominantAxes(inverseMatrix).at(worldAxis); ScalarType viewSpacing = geometry3D->GetSpacing()[dominantAxis]; /// Although the double value returned by GetExtent() holds a round number, /// you need to add 0.5 to safely convert it to unsigned it. I have seen a /// case when the result was less by one without this. auto slices = static_cast(geometry3D->GetExtent(dominantAxis) + 0.5); if ( slices == 0 && geometry3D->GetExtent(dominantAxis) > 0) { // require at least one slice if there is _some_ extent slices = 1; } #ifndef NDEBUG int upDirection = itk::Function::Sign(inverseMatrix[dominantAxis][worldAxis]); /// The normal vector of an imaginary plane that points from the world origin (bottom left back /// corner or the world, with the lowest physical coordinates) towards the inside of the volume, /// along the renderer axis. Length is the slice thickness. Vector3D worldPlaneNormal = inverseMatrix.get_row(dominantAxis) * (upDirection * viewSpacing); /// The normal of the standard plane geometry just created. Vector3D standardPlaneNormal = planeGeometry->GetNormal(); /// The standard plane must be parallel to the 'world plane'. The normal of the standard plane /// must point against the world plane if and only if 'top' is 'false'. The length of the /// standard plane normal must be equal to the slice thickness. assert((standardPlaneNormal - (top ? 1.0 : -1.0) * worldPlaneNormal).GetSquaredNorm() < 0.000001); #endif this->InitializeEvenlySpaced(planeGeometry, viewSpacing, slices); #ifndef NDEBUG /// The standard plane normal and the z axis vector of the sliced geometry must point in /// the same direction. Vector3D zAxisVector = this->GetAxisVector(2); Vector3D upscaledStandardPlaneNormal = standardPlaneNormal; upscaledStandardPlaneNormal *= slices; assert((zAxisVector - upscaledStandardPlaneNormal).GetSquaredNorm() < 0.000001); /// You can use this test is to check the handedness of the coordinate system of the current /// geometry. In principle, you can use either left- or right-handed coordinate systems, but /// you normally want it to be consistent, that is the handedness should be the same across /// the renderers of the same viewer. // ScalarType det = vnl_det(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix()); // MITK_DEBUG << "world axis: " << worldAxis << (det > 0 ? " ; right-handed" : " ; left-handed"); #endif } void mitk::SlicedGeometry3D::ReinitializePlanes(const Point3D ¢er, const Point3D &referencePoint) { // Need a reference frame to align the rotated planes if (!m_ReferenceGeometry) { return; } // Get first plane of plane stack PlaneGeometry *firstPlane = m_PlaneGeometries[0]; // If plane stack is empty, exit if (!firstPlane || dynamic_cast(firstPlane)) { return; } // Calculate the "directed" spacing when taking the plane (defined by its axes // vectors and normal) as the reference coordinate frame. // // This is done by calculating the radius of the ellipsoid defined by the // original volume spacing axes, in the direction of the respective axis of the // reference frame. mitk::Vector3D axis0 = firstPlane->GetAxisVector(0); mitk::Vector3D axis1 = firstPlane->GetAxisVector(1); mitk::Vector3D normal = firstPlane->GetNormal(); normal.Normalize(); Vector3D spacing; spacing[0] = this->CalculateSpacing(axis0); spacing[1] = this->CalculateSpacing(axis1); spacing[2] = this->CalculateSpacing(normal); Superclass::SetSpacing(spacing); // Now we need to calculate the number of slices in the plane's normal // direction, so that the entire volume is covered. This is done by first // calculating the dot product between the volume diagonal (the maximum // distance inside the volume) and the normal, and dividing this value by // the directed spacing calculated above. ScalarType directedExtent = std::abs(m_ReferenceGeometry->GetExtentInMM(0) * normal[0]) + std::abs(m_ReferenceGeometry->GetExtentInMM(1) * normal[1]) + std::abs(m_ReferenceGeometry->GetExtentInMM(2) * normal[2]); if (directedExtent >= spacing[2]) { m_Slices = static_cast(directedExtent / spacing[2] + 0.5); } else { m_Slices = 1; } // The origin of our "first plane" needs to be adapted to this new extent. // To achieve this, we first calculate the current distance to the volume's // center, and then shift the origin in the direction of the normal by the // difference between this distance and half of the new extent. double centerOfRotationDistance = firstPlane->SignedDistanceFromPlane(center); if (centerOfRotationDistance > 0) { firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * (centerOfRotationDistance - directedExtent / 2.0)); m_DirectionVector = normal; } else { firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * (directedExtent / 2.0 + centerOfRotationDistance)); m_DirectionVector = -normal; } // Now we adjust this distance according with respect to the given reference // point: we need to make sure that the point is touched by one slice of the // new slice stack. double referencePointDistance = firstPlane->SignedDistanceFromPlane(referencePoint); auto referencePointSlice = static_cast(referencePointDistance / spacing[2]); double alignmentValue = referencePointDistance / spacing[2] - referencePointSlice; firstPlane->SetOrigin(firstPlane->GetOrigin() + normal * alignmentValue * spacing[2]); // Finally, we can clear the previous geometry stack and initialize it with // our re-initialized "first plane". m_PlaneGeometries.assign(m_Slices, PlaneGeometry::Pointer(nullptr)); if (m_Slices > 0) { m_PlaneGeometries[0] = firstPlane; } // Reinitialize SNC with new number of slices m_SliceNavigationController->GetSlice()->SetSteps(m_Slices); this->Modified(); } double mitk::SlicedGeometry3D::CalculateSpacing(const mitk::Vector3D &d) const { // Need the spacing of the underlying dataset / geometry if (!m_ReferenceGeometry) { return 1.0; } const mitk::Vector3D &spacing = m_ReferenceGeometry->GetSpacing(); return SlicedGeometry3D::CalculateSpacing(spacing, d); } double mitk::SlicedGeometry3D::CalculateSpacing(const mitk::Vector3D &spacing, const mitk::Vector3D &d) { // The following can be derived from the ellipsoid equation // // 1 = x^2/a^2 + y^2/b^2 + z^2/c^2 // // where (a,b,c) = spacing of original volume (ellipsoid radii) // and (x,y,z) = scaled coordinates of vector d (according to ellipsoid) // double scaling = d[0] * d[0] / (spacing[0] * spacing[0]) + d[1] * d[1] / (spacing[1] * spacing[1]) + d[2] * d[2] / (spacing[2] * spacing[2]); scaling = sqrt(scaling); return (sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]) / scaling); } mitk::Vector3D mitk::SlicedGeometry3D::AdjustNormal(const mitk::Vector3D &normal) const { TransformType::Pointer inverse = TransformType::New(); m_ReferenceGeometry->GetIndexToWorldTransform()->GetInverse(inverse); Vector3D transformedNormal = inverse->TransformVector(normal); transformedNormal.Normalize(); return transformedNormal; } void mitk::SlicedGeometry3D::SetImageGeometry(const bool isAnImageGeometry) { Superclass::SetImageGeometry(isAnImageGeometry); unsigned int s; for (s = 0; s < m_Slices; ++s) { mitk::BaseGeometry *geometry = m_PlaneGeometries[s]; if (geometry != nullptr) { geometry->SetImageGeometry(isAnImageGeometry); } } } void mitk::SlicedGeometry3D::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) { unsigned int s; for (s = 0; s < m_Slices; ++s) { mitk::BaseGeometry *geometry = m_PlaneGeometries[s]; if (geometry != nullptr) { geometry->ChangeImageGeometryConsideringOriginOffset(isAnImageGeometry); } } Superclass::ChangeImageGeometryConsideringOriginOffset(isAnImageGeometry); } bool mitk::SlicedGeometry3D::IsValidSlice(int s) const { return ((s >= 0) && (s < (int)m_Slices)); } const mitk::BaseGeometry *mitk::SlicedGeometry3D::GetReferenceGeometry() const { return m_ReferenceGeometry; } void mitk::SlicedGeometry3D::SetReferenceGeometry(const BaseGeometry *referenceGeometry) { m_ReferenceGeometry = referenceGeometry; std::vector::iterator it; for (it = m_PlaneGeometries.begin(); it != m_PlaneGeometries.end(); ++it) { (*it)->SetReferenceGeometry(referenceGeometry); } } bool mitk::SlicedGeometry3D::HasReferenceGeometry() const { return ( m_ReferenceGeometry != nullptr ); } void mitk::SlicedGeometry3D::PreSetSpacing(const mitk::Vector3D &aSpacing) { bool hasEvenlySpacedPlaneGeometry = false; mitk::Point3D origin; mitk::Vector3D rightDV, bottomDV; BoundingBox::BoundsArrayType bounds; // Check for valid spacing if (!(aSpacing[0] > 0 && aSpacing[1] > 0 && aSpacing[2] > 0)) { mitkThrow() << "You try to set a spacing with at least one element equal or " "smaller to \"0\". This might lead to a crash during rendering. Please double" " check your data!"; } // In case of evenly-spaced data: re-initialize instances of PlaneGeometry, // since the spacing influences them if ((m_EvenlySpaced) && (m_PlaneGeometries.size() > 0)) { const PlaneGeometry *planeGeometry = m_PlaneGeometries[0]; if (planeGeometry && !dynamic_cast(planeGeometry)) { this->WorldToIndex(planeGeometry->GetOrigin(), origin); this->WorldToIndex(planeGeometry->GetAxisVector(0), rightDV); this->WorldToIndex(planeGeometry->GetAxisVector(1), bottomDV); bounds = planeGeometry->GetBounds(); hasEvenlySpacedPlaneGeometry = true; } } BaseGeometry::_SetSpacing(aSpacing); mitk::PlaneGeometry::Pointer firstGeometry; // In case of evenly-spaced data: re-initialize instances of PlaneGeometry, // since the spacing influences them if (hasEvenlySpacedPlaneGeometry) { // create planeGeometry according to new spacing this->IndexToWorld(origin, origin); this->IndexToWorld(rightDV, rightDV); this->IndexToWorld(bottomDV, bottomDV); mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->SetImageGeometry(this->GetImageGeometry()); planeGeometry->SetReferenceGeometry(m_ReferenceGeometry); // Store spacing, as Initialize... needs a pointer mitk::Vector3D lokalSpacing = this->GetSpacing(); planeGeometry->InitializeStandardPlane(rightDV.GetVnlVector(), bottomDV.GetVnlVector(), &lokalSpacing); planeGeometry->SetOrigin(origin); planeGeometry->SetBounds(bounds); firstGeometry = planeGeometry; } else if ((m_EvenlySpaced) && (m_PlaneGeometries.size() > 0)) { firstGeometry = m_PlaneGeometries[0].GetPointer(); } // clear and reserve PlaneGeometry::Pointer gnull = nullptr; m_PlaneGeometries.assign(m_Slices, gnull); if (m_Slices > 0) { m_PlaneGeometries[0] = firstGeometry; } this->Modified(); } void mitk::SlicedGeometry3D::SetSliceNavigationController(SliceNavigationController *snc) { m_SliceNavigationController = snc; } mitk::SliceNavigationController *mitk::SlicedGeometry3D::GetSliceNavigationController() { return m_SliceNavigationController; } void mitk::SlicedGeometry3D::SetEvenlySpaced(bool on) { if (m_EvenlySpaced != on) { m_EvenlySpaced = on; this->Modified(); } } void mitk::SlicedGeometry3D::SetDirectionVector(const mitk::Vector3D &directionVector) { Vector3D newDir = directionVector; newDir.Normalize(); if (newDir != m_DirectionVector) { m_DirectionVector = newDir; this->Modified(); } } // void // mitk::SlicedGeometry3D::SetTimeBounds( const mitk::TimeBounds& timebounds ) //{ // Superclass::SetTimeBounds( timebounds ); // // unsigned int s; // for ( s = 0; s < m_Slices; ++s ) // { // if(m_Geometry2Ds[s].IsNotNull()) // { // m_Geometry2Ds[s]->SetTimeBounds( timebounds ); // } // } // m_TimeBounds = timebounds; //} itk::LightObject::Pointer mitk::SlicedGeometry3D::InternalClone() const { Self::Pointer newGeometry = new SlicedGeometry3D(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } void mitk::SlicedGeometry3D::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << " EvenlySpaced: " << m_EvenlySpaced << std::endl; if (m_EvenlySpaced) { os << indent << " DirectionVector: " << m_DirectionVector << std::endl; } os << indent << " Slices: " << m_Slices << std::endl; os << std::endl; os << indent << " GetPlaneGeometry(0): "; if (this->GetPlaneGeometry(0) == nullptr) { os << "nullptr" << std::endl; } else { this->GetPlaneGeometry(0)->Print(os, indent); } } void mitk::SlicedGeometry3D::ExecuteOperation(Operation *operation) { PlaneGeometry::Pointer geometry2D; ApplyTransformMatrixOperation *applyMatrixOp; Point3D center; switch (operation->GetOperationType()) { case OpNOTHING: break; case OpROTATE: if (m_EvenlySpaced) { // Need a reference frame to align the rotation if (m_ReferenceGeometry) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Save first slice PlaneGeometry::Pointer geometry2D = m_PlaneGeometries[0]; auto *rotOp = dynamic_cast(operation); // Generate a RotationOperation using the dataset center instead of // the supplied rotation center. This is necessary so that the rotated // zero-plane does not shift away. The supplied center is instead used // to adjust the slice stack afterwards. Point3D center = m_ReferenceGeometry->GetCenter(); RotationOperation centeredRotation( rotOp->GetOperationType(), center, rotOp->GetVectorOfRotation(), rotOp->GetAngleOfRotation()); // Rotate first slice geometry2D->ExecuteOperation(¢eredRotation); // Clear the slice stack and adjust it according to the center of // the dataset and the supplied rotation center (see documentation of // ReinitializePlanes) this->ReinitializePlanes(center, rotOp->GetCenterOfRotation()); geometry2D->SetSpacing(this->GetSpacing()); if (m_SliceNavigationController) { m_SliceNavigationController->SelectSliceByPoint(rotOp->GetCenterOfRotation()); m_SliceNavigationController->AdjustSliceStepperRange(); } BaseGeometry::ExecuteOperation(¢eredRotation); } else { // we also have to consider the case, that there is no reference geometry available. if (m_PlaneGeometries.size() > 0) { // Reach through to all slices in my container for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { // Test for empty slices, which can happen if evenly spaced geometry if ((*iter).IsNotNull()) { (*iter)->ExecuteOperation(operation); } } // rotate overall geometry auto *rotOp = dynamic_cast(operation); BaseGeometry::ExecuteOperation(rotOp); } } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpORIENT: if (m_EvenlySpaced) { // get operation data auto *planeOp = dynamic_cast(operation); // Get first slice PlaneGeometry::Pointer planeGeometry = m_PlaneGeometries[0]; // Need a PlaneGeometry, a PlaneOperation and a reference frame to // carry out the re-orientation. If not all avaialble, stop here if (!m_ReferenceGeometry || (!planeGeometry || dynamic_cast(planeGeometry.GetPointer())) || !planeOp) { break; } // General Behavior: // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // // 1st Step: Reorient Normal Vector of first plane // Point3D center = planeOp->GetPoint(); // m_ReferenceGeometry->GetCenter(); mitk::Vector3D currentNormal = planeGeometry->GetNormal(); mitk::Vector3D newNormal; if (planeOp->AreAxisDefined()) { // If planeOp was defined by one centerpoint and two axis vectors newNormal = CrossProduct(planeOp->GetAxisVec0(), planeOp->GetAxisVec1()); } else { // If planeOp was defined by one centerpoint and one normal vector newNormal = planeOp->GetNormal(); } // Get Rotation axis und angle currentNormal.Normalize(); newNormal.Normalize(); ScalarType rotationAngle = angle(currentNormal.GetVnlVector(), newNormal.GetVnlVector()); rotationAngle *= 180.0 / vnl_math::pi; // from rad to deg Vector3D rotationAxis = itk::CrossProduct(currentNormal, newNormal); if (std::abs(rotationAngle - 180) < mitk::eps) { // current Normal and desired normal are not linear independent!!(e.g 1,0,0 and -1,0,0). // Rotation Axis should be ANY vector that is 90� to current Normal mitk::Vector3D helpNormal; helpNormal = currentNormal; helpNormal[0] += 1; helpNormal[1] -= 1; helpNormal[2] += 1; helpNormal.Normalize(); rotationAxis = itk::CrossProduct(helpNormal, currentNormal); } RotationOperation centeredRotation(mitk::OpROTATE, center, rotationAxis, rotationAngle); // Rotate first slice planeGeometry->ExecuteOperation(¢eredRotation); // Reinitialize planes and select slice, if my rotations are all done. if (!planeOp->AreAxisDefined()) { // Clear the slice stack and adjust it according to the center of // rotation and plane position (see documentation of ReinitializePlanes) this->ReinitializePlanes(center, planeOp->GetPoint()); planeGeometry->SetSpacing(this->GetSpacing()); if (m_SliceNavigationController) { m_SliceNavigationController->SelectSliceByPoint(planeOp->GetPoint()); m_SliceNavigationController->AdjustSliceStepperRange(); } } // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) BaseGeometry::ExecuteOperation(¢eredRotation); // // 2nd step. If axis vectors were defined, rotate the plane around its normal to fit these // if (planeOp->AreAxisDefined()) { mitk::Vector3D vecAxixNew = planeOp->GetAxisVec0(); vecAxixNew.Normalize(); mitk::Vector3D VecAxisCurr = planeGeometry->GetAxisVector(0); VecAxisCurr.Normalize(); ScalarType rotationAngle = angle(VecAxisCurr.GetVnlVector(), vecAxixNew.GetVnlVector()); rotationAngle = rotationAngle * 180 / PI; // Rad to Deg // we rotate around the normal of the plane, but we do not know, if we need to rotate clockwise // or anti-clockwise. So we rotate around the crossproduct of old and new Axisvector. // Since both axis vectors lie in the plane, the crossproduct is the planes normal or the negative planes // normal rotationAxis = itk::CrossProduct(VecAxisCurr, vecAxixNew); if (std::abs(rotationAngle - 180) < mitk::eps) { // current axisVec and desired axisVec are not linear independent!!(e.g 1,0,0 and -1,0,0). // Rotation Axis can be just plane Normal. (have to rotate by 180�) rotationAxis = newNormal; } // Perfom Rotation mitk::RotationOperation op(mitk::OpROTATE, center, rotationAxis, rotationAngle); planeGeometry->ExecuteOperation(&op); // Apply changes on first slice to whole slice stack this->ReinitializePlanes(center, planeOp->GetPoint()); planeGeometry->SetSpacing(this->GetSpacing()); if (m_SliceNavigationController) { m_SliceNavigationController->SelectSliceByPoint(planeOp->GetPoint()); m_SliceNavigationController->AdjustSliceStepperRange(); } // Also apply rotation on the slicedGeometry - Geometry3D (Bounding geometry) BaseGeometry::ExecuteOperation(&op); } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpRESTOREPLANEPOSITION: if (m_EvenlySpaced) { // Save first slice PlaneGeometry::Pointer planeGeometry = m_PlaneGeometries[0]; auto *restorePlaneOp = dynamic_cast(operation); // Need a PlaneGeometry, a PlaneOperation and a reference frame to // carry out the re-orientation if (m_ReferenceGeometry && (planeGeometry && dynamic_cast(planeGeometry.GetPointer()) == nullptr) && restorePlaneOp) { // Clear all generated geometries and then rotate only the first slice. // The other slices will be re-generated on demand // Rotate first slice planeGeometry->ExecuteOperation(restorePlaneOp); m_DirectionVector = restorePlaneOp->GetDirectionVector(); double centerOfRotationDistance = planeGeometry->SignedDistanceFromPlane(m_ReferenceGeometry->GetCenter()); if (centerOfRotationDistance <= 0) { m_DirectionVector = -m_DirectionVector; } Vector3D spacing = restorePlaneOp->GetSpacing(); Superclass::SetSpacing(spacing); // /*Now we need to calculate the number of slices in the plane's normal // direction, so that the entire volume is covered. This is done by first // calculating the dot product between the volume diagonal (the maximum // distance inside the volume) and the normal, and dividing this value by // the directed spacing calculated above.*/ ScalarType directedExtent = std::abs(m_ReferenceGeometry->GetExtentInMM(0) * m_DirectionVector[0]) + std::abs(m_ReferenceGeometry->GetExtentInMM(1) * m_DirectionVector[1]) + std::abs(m_ReferenceGeometry->GetExtentInMM(2) * m_DirectionVector[2]); if (directedExtent >= spacing[2]) { m_Slices = static_cast(directedExtent / spacing[2] + 0.5); } else { m_Slices = 1; } m_PlaneGeometries.assign(m_Slices, PlaneGeometry::Pointer(nullptr)); if (m_Slices > 0) { m_PlaneGeometries[0] = planeGeometry; } m_SliceNavigationController->GetSlice()->SetSteps(m_Slices); this->Modified(); // End Reinitialization if (m_SliceNavigationController) { m_SliceNavigationController->GetSlice()->SetPos(restorePlaneOp->GetPos()); m_SliceNavigationController->AdjustSliceStepperRange(); } BaseGeometry::ExecuteOperation(restorePlaneOp); } } else { // Reach through to all slices for (auto iter = m_PlaneGeometries.begin(); iter != m_PlaneGeometries.end(); ++iter) { (*iter)->ExecuteOperation(operation); } } break; case OpAPPLYTRANSFORMMATRIX: // Clear all generated geometries and then transform only the first slice. // The other slices will be re-generated on demand // Save first slice geometry2D = m_PlaneGeometries[0]; applyMatrixOp = dynamic_cast(operation); // Apply transformation to first plane geometry2D->ExecuteOperation(applyMatrixOp); // Generate a ApplyTransformMatrixOperation using the dataset center instead of // the supplied rotation center. The supplied center is instead used to adjust the // slice stack afterwards (see OpROTATE). center = m_ReferenceGeometry->GetCenter(); // Clear the slice stack and adjust it according to the center of // the dataset and the supplied rotation center (see documentation of // ReinitializePlanes) this->ReinitializePlanes(center, applyMatrixOp->GetReferencePoint()); BaseGeometry::ExecuteOperation(applyMatrixOp); break; default: // let handle by base class if we don't do anything BaseGeometry::ExecuteOperation(operation); } this->Modified(); } diff --git a/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp b/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp new file mode 100644 index 0000000000..6bc57462bc --- /dev/null +++ b/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp @@ -0,0 +1,380 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include + +#include "mitkSourceImageRelationRule.h" +#include "mitkPropertyNameHelper.h" +#include "mitkStringProperty.h" +#include "mitkTemporoSpatialStringProperty.h" +#include "mitkDataNode.h" + + +bool mitk::SourceImageRelationRule::IsAbstract() const +{ + return m_PurposeTag.empty(); +}; + +bool mitk::SourceImageRelationRule::IsSupportedRuleID(const RuleIDType& ruleID) const +{ + return ruleID == this->GetRuleID() || (IsAbstract() && ruleID.find("SourceImageRelation ") == 0); +}; + +mitk::SourceImageRelationRule::RuleIDType mitk::SourceImageRelationRule::GetRuleID() const +{ + return "SourceImageRelation " + m_PurposeTag; +}; + +std::string mitk::SourceImageRelationRule::GetDisplayName() const +{ + return m_DisplayName; +}; + +std::string mitk::SourceImageRelationRule::GetSourceRoleName() const +{ + return m_SourceRole; +}; + +std::string mitk::SourceImageRelationRule::GetDestinationRoleName() const +{ + return m_DestinationRole; +}; + +bool mitk::SourceImageRelationRule::IsDestinationCandidate(const IPropertyProvider *owner) const +{ + auto node = dynamic_cast(owner); + + auto image = nullptr != node + ? dynamic_cast(node->GetData()) + : dynamic_cast(owner); + + return image != nullptr; +} + +mitk::SourceImageRelationRule::RelationUIDType mitk::SourceImageRelationRule::Connect(Image *source, const Image *destination) const +{ + return Superclass::Connect(source, destination); +}; + +mitk::SourceImageRelationRule::SourceImageRelationRule() + : m_PurposeTag(""), m_DisplayName("Abstract image to image relation"), m_SourceRole("derived data"), m_DestinationRole("source image") +{}; + +mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag) + : SourceImageRelationRule(purposeTag, purposeTag + " relation"){}; + +mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName) + : SourceImageRelationRule( + purposeTag, displayName, "derived data", "source image"){}; + +mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag, + const std::string &displayName, + const std::string &sourceRole, + const std::string &destinationRole) + : m_PurposeTag(purposeTag), m_DisplayName(displayName), m_SourceRole(sourceRole), m_DestinationRole(destinationRole){}; + +mitk::SourceImageRelationRule::InstanceIDVectorType mitk::SourceImageRelationRule::GetInstanceID_datalayer( + const IPropertyProvider * source, const IPropertyProvider * destination) const +{ + InstanceIDVectorType result; + + auto relevantReferenceIndices = GetReferenceSequenceIndices(source, destination); + + auto itemRegExStr = this->GetRIIPropertyRegEx("SourceImageSequenceItem"); + auto regEx = std::regex(itemRegExStr); + + //workaround until T24729 is done. Please remove if T24728 is done + auto keys = PropertyRelationRuleBase::GetPropertyKeys(source); + //end workaround for T24729 + + for (const auto &key : keys) + { + if (std::regex_match(key, regEx)) + { + auto sequItemProp = source->GetConstProperty(key); + if (sequItemProp.IsNotNull()) + { + auto finding = std::find(std::cbegin(relevantReferenceIndices), std::cend(relevantReferenceIndices), sequItemProp->GetValueAsString()); + if (finding != std::cend(relevantReferenceIndices)) + { + auto instanceID = GetInstanceIDByPropertyName(key); + auto ruleID = GetRuleIDByInstanceID(source, instanceID); + if (this->IsSupportedRuleID(ruleID)) + { + result.push_back(instanceID); + } + } + } + } + } + + return result; +}; + +std::vector mitk::SourceImageRelationRule::GetReferenceSequenceIndices(const IPropertyProvider * source, + const IPropertyProvider * destination) const +{ + std::vector result; + + BaseProperty::ConstPointer destInstanceUIDProp; + + if (destination) + { + destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0018)); + + if (destInstanceUIDProp.IsNull()) + { + return result; + } + } + + PropertyKeyPath referencedInstanceUIDs; + referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); + + auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);; + auto regEx = std::regex(sourceRegExStr); + + std::vector keys; + //workaround until T24729 is done. Please remove if T24728 is done + keys = PropertyRelationRuleBase::GetPropertyKeys(source); + //end workaround for T24729 + + for (const auto &key : keys) + { + if (std::regex_match(key, regEx)) + { + auto refUIDProp = source->GetConstProperty(key); + if (destination==nullptr || *refUIDProp == *destInstanceUIDProp) + { + auto currentKeyPath = PropertyNameToPropertyKeyPath(key); + auto currentKeyPathSelection = currentKeyPath.GetNode(2).selection; + PropertyKeyPath purposePath; + purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", currentKeyPathSelection).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); + + auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); + if (this->IsAbstract() || (purposeProp.IsNotNull() && purposeProp->GetValueAsString() == this->m_PurposeTag)) + { + result.push_back(std::to_string(currentKeyPathSelection)); + } + } + } + } + + return result; +}; + +bool mitk::SourceImageRelationRule::HasImplicitDataRelation(const IPropertyProvider * source, + const IPropertyProvider * destination) const +{ + auto relevantReferences = GetReferenceSequenceIndices(source, destination); + + if (this->IsAbstract()) + { + return !relevantReferences.empty(); + } + else + { + for (auto referenceIndex : relevantReferences) + { + PropertyKeyPath purposePath; + purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", std::stoi(referenceIndex)).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); + auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); + + if (purposeProp.IsNotNull() && purposeProp->GetValueAsString() == this->m_PurposeTag) + { + return true; + } + } + } + + return false; +}; + + +/**This mutex is used to guard mitk::SourceImageRelationRule::CreateNewSourceImageSequenceItem by a class wide mutex to avoid +racing conditions in a scenario where rules are used concurrently. It is not in the class interface itself, because it +is an implementation detail. +*/ +namespace +{ + std::mutex sequenceItemCreationLock; +} + +mitk::PropertyKeyPath::ItemSelectionIndex mitk::SourceImageRelationRule::CreateNewSourceImageSequenceItem( + IPropertyOwner *source) const +{ + std::lock_guard guard(sequenceItemCreationLock); + + ////////////////////////////////////// + // Get all existing sequence items + + std::vector instanceIDs; + PropertyKeyPath::ItemSelectionIndex newID = 0; + + PropertyKeyPath referencedInstanceUIDs; + referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); + auto regExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs); + auto regEx = std::regex(regExStr); + std::smatch instance_matches; + + //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. + const auto keys = GetPropertyKeys(source); + //end workaround for T24729 + + for (const auto &key : keys) + { + if (std::regex_search(key, instance_matches, regEx)) + { + if (instance_matches.size()>1) + { + instanceIDs.push_back(std::stoi(instance_matches[1])); + } + } + } + + ////////////////////////////////////// + // Get new ID + + std::sort(instanceIDs.begin(), instanceIDs.end()); + if (!instanceIDs.empty()) + { + newID = instanceIDs.back()+1; + } + + ////////////////////////////////////// + // reserve new ID + + PropertyKeyPath newSourceImageSequencePath; + newSourceImageSequencePath.AddElement("DICOM").AddElement("0008").AddSelection("2112",newID).AddElement("0008").AddElement("1155"); + + auto newKey = + PropertyKeyPathToPropertyName(newSourceImageSequencePath); + source->SetProperty(newKey, mitk::TemporoSpatialStringProperty::New("reserved slot for source image sequence")); + + return newID; +}; + + +void mitk::SourceImageRelationRule::Connect_datalayer(IPropertyOwner * source, + const IPropertyProvider * destination, + const InstanceIDType & instanceID) const +{ + auto destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008,0x0018)); + auto destClassUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0016)); + + if (destInstanceUIDProp.IsNotNull() && destClassUIDProp.IsNotNull()) + { + auto existingRefs = this->GetReferenceSequenceIndices(source, destination); + std::string newSelectionIndexStr; + if (!existingRefs.empty()) + { + newSelectionIndexStr = existingRefs[0]; + } + else + { + PropertyKeyPath::ItemSelectionIndex newSelectionIndex = CreateNewSourceImageSequenceItem(source); + + PropertyKeyPath refInstanceUIDPath; + refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1155"); + source->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), destInstanceUIDProp->Clone()); + + PropertyKeyPath refClassUIDPath; + refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1150"); + source->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), destClassUIDProp->Clone()); + + PropertyKeyPath purposePath; + purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); + source->SetProperty(PropertyKeyPathToPropertyName(purposePath), StringProperty::New(m_PurposeTag)); + + newSelectionIndexStr = std::to_string(newSelectionIndex); + } + + auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem"); + source->SetProperty(PropertyKeyPathToPropertyName(sourceImageRefPath), StringProperty::New(newSelectionIndexStr).GetPointer()); + } + else + { + MITK_DEBUG << "Cannot connect SourceImageRelationRule on data layer. Passed destination does not have properties for DICOM SOP Instance UIDs(0x0008, 0x0018) and DICOM SOP Class UID(0x0008, 0x0016)"; + } +}; + +void mitk::SourceImageRelationRule::Disconnect_datalayer(IPropertyOwner * source, const InstanceIDType & instanceID) const +{ + auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem"); + auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath)); + + if (imageRefProp.IsNotNull()) + { + auto deletedImageRefSequenceIndex = imageRefProp->GetValueAsString(); + + auto refs = GetReferenceSequenceIndices(source); + std::sort(refs.begin(), refs.end()); + + for (auto refIndexStr : refs) + { + auto refIndex = std::stoi(refIndexStr); + + if (refIndex >= std::stoi(deletedImageRefSequenceIndex)) + { + PropertyKeyPath refDICOMDataPath; + refDICOMDataPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", refIndex); + auto prefix = PropertyKeyPathToPropertyName(refDICOMDataPath); + + PropertyKeyPath refRelDataPath = GetRootKeyPath().AddAnyElement().AddElement("SourceImageSequenceItem");; + auto regEx = std::regex(PropertyKeyPathToPropertyRegEx(refRelDataPath)); + + //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. + const auto keys = GetPropertyKeys(source); + //end workaround for T24729 + + for (const auto &key : keys) + { + if (key.find(prefix) == 0) + { //its a relevant DICOM property delete or update + if (refIndexStr != deletedImageRefSequenceIndex) + { + //reindex to close the gap in the dicom sequence. + auto newPath = PropertyNameToPropertyKeyPath(key); + newPath.GetNode(2).selection = refIndex - 1; + source->SetProperty(PropertyKeyPathToPropertyName(newPath), source->GetNonConstProperty(key)); + } + //remove old/outdated data layer information + source->RemoveProperty(key); + + auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem"); + } + if (std::regex_match(key, regEx)) + { + auto imageSequenceItemProp = source->GetConstProperty(key); + if (imageSequenceItemProp->GetValueAsString() == std::to_string(refIndex)) + { + //its a relevant data property of the relation rule. + source->SetProperty(key, StringProperty::New(std::to_string(refIndex - 1))); + } + } + } + } + } + } +}; + +itk::LightObject::Pointer mitk::SourceImageRelationRule::InternalClone() const +{ + itk::LightObject::Pointer result = Self::New(this->m_PurposeTag, this->m_DisplayName, this->m_SourceRole, this->m_DestinationRole).GetPointer(); + + return result; +}; diff --git a/Modules/Core/test/files.cmake b/Modules/Core/test/files.cmake index 0b6664b2a2..3c8a6f19dc 100644 --- a/Modules/Core/test/files.cmake +++ b/Modules/Core/test/files.cmake @@ -1,200 +1,201 @@ # tests with no extra command line parameter set(MODULE_TESTS # IMPORTANT: If you plan to deactivate / comment out a test please write a bug number to the commented out line of code. # # Example: #mitkMyTest #this test is commented out because of bug 12345 # # It is important that the bug is open and that the test will be activated again before the bug is closed. This assures that # no test is forgotten after it was commented out. If there is no bug for your current problem, please add a new one and # mark it as critical. ################## DISABLED TESTS ################################################# #mitkAbstractTransformGeometryTest.cpp #seems as tested class mitkExternAbstractTransformGeometry doesnt exist any more #mitkStateMachineContainerTest.cpp #rewrite test, indirect since no longer exported Bug 14529 #mitkRegistrationBaseTest.cpp #tested class mitkRegistrationBase doesn't exist any more #mitkSegmentationInterpolationTest.cpp #file doesn't exist! #mitkPipelineSmartPointerCorrectnessTest.cpp #file doesn't exist! #mitkITKThreadingTest.cpp #test outdated because itk::Semaphore was removed from ITK #mitkAbstractTransformPlaneGeometryTest.cpp #mitkVtkAbstractTransformPlaneGeometry doesn't exist any more #mitkTestUtilSharedLibrary.cpp #Linker problem with this test... #mitkTextOverlay2DSymbolsRenderingTest.cpp #Implementation of the tested feature is not finished yet. Ask Christoph or see bug 15104 for details. ################# RUNNING TESTS ################################################### mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkDataNodeTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkDispatcherTest.cpp mitkEnumerationPropertyTest.cpp mitkFileReaderRegistryTest.cpp #mitkFileWriterRegistryTest.cpp mitkFloatToStringTest.cpp mitkGenericPropertyTest.cpp mitkGeometry3DTest.cpp mitkGeometry3DEqualTest.cpp mitkGeometryDataIOTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkImageCastTest.cpp mitkImageEqualTest.cpp mitkImageDataItemTest.cpp mitkImageGeneratorTest.cpp mitkIOUtilTest.cpp mitkBaseDataTest.cpp mitkImportItkImageTest.cpp mitkGrabItkImageMemoryTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetTest.cpp mitkPointSetEqualTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetOnEmptyTest.cpp mitkPointSetLocaleTest.cpp mitkPointSetWriterTest.cpp mitkPointSetReaderTest.cpp mitkPointSetPointOperationsTest.cpp mitkProgressBarTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp mitkPropertyPersistenceTest.cpp mitkPropertyPersistenceInfoTest.cpp mitkPropertyRelationRuleBaseTest.cpp mitkPropertyRelationsTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkSurfaceTest.cpp mitkSurfaceEqualTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeGeometryTest.cpp mitkProportionalTimeGeometryTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp mitkStepperTest.cpp mitkRenderingManagerTest.cpp mitkCompositePixelValueToStringTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkNodePredicateDataPropertyTest.cpp mitkNodePredicateFunctionTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp mitkLogTest.cpp mitkImageDimensionConverterTest.cpp mitkLoggingAdapterTest.cpp mitkUIDGeneratorTest.cpp mitkPlanePositionManagerTest.cpp mitkAffineTransformBaseTest.cpp mitkPropertyAliasesTest.cpp mitkPropertyDescriptionsTest.cpp mitkPropertyExtensionsTest.cpp mitkPropertyFiltersTest.cpp mitkPropertyKeyPathTest.cpp mitkTinyXMLTest.cpp mitkRawImageFileReaderTest.cpp mitkInteractionEventTest.cpp mitkLookupTableTest.cpp mitkSTLFileReaderTest.cpp mitkPointTypeConversionTest.cpp mitkVectorTypeConversionTest.cpp mitkMatrixTypeConversionTest.cpp mitkArrayTypeConversionTest.cpp mitkSurfaceToImageFilterTest.cpp mitkBaseGeometryTest.cpp mitkImageToSurfaceFilterTest.cpp mitkEqualTest.cpp mitkLineTest.cpp mitkArbitraryTimeGeometryTest mitkItkImageIOTest.cpp mitkRotatedSlice4DTest.cpp mitkLevelWindowManagerCppUnitTest.cpp mitkVectorPropertyTest.cpp mitkTemporoSpatialStringPropertyTest.cpp mitkPropertyNameHelperTest.cpp mitkNodePredicateGeometryTest.cpp mitkPreferenceListReaderOptionsFunctorTest.cpp mitkGenericIDRelationRuleTest.cpp + mitkSourceImageRelationRuleTest.cpp ) if(MITK_ENABLE_RENDERING_TESTING) set(MODULE_TESTS ${MODULE_TESTS} mitkPlaneGeometryDataMapper2DTest.cpp mitkPointSetDataInteractorTest.cpp #since mitkInteractionTestHelper is currently creating a vtkRenderWindow mitkSurfaceVtkMapper2DTest.cpp #new rendering test in CppUnit style mitkSurfaceVtkMapper2D3DTest.cpp # comparisons/consistency 2D/3D ) endif() # test with image filename as an extra command line parameter set(MODULE_IMAGE_TESTS mitkImageTimeSelectorTest.cpp #only runs on images mitkImageAccessorTest.cpp #only runs on images ) set(MODULE_SURFACE_TESTS mitkSurfaceVtkWriterTest.cpp #only runs on surfaces ) # list of images for which the tests are run set(MODULE_TESTIMAGE US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) set(MODULE_TESTSURFACE binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS mitkDataStorageTest.cpp mitkDataNodeTest.cpp mitkEventConfigTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp mitkImageVtkMapper2DColorTest.cpp mitkImageVtkMapper2DSwivelTest.cpp mitkImageVtkMapper2DTransferFunctionTest.cpp mitkImageVtkMapper2DOpacityTransferFunctionTest.cpp mitkImageVtkMapper2DLookupTableTest.cpp mitkSurfaceVtkMapper3DTest mitkVolumeCalculatorTest.cpp mitkLevelWindowManagerTest.cpp mitkPointSetVtkMapper2DTest.cpp mitkPointSetVtkMapper2DImageTest.cpp mitkPointSetVtkMapper2DGlyphTypeTest.cpp mitkPointSetVtkMapper2DTransformedPointsTest.cpp mitkVTKRenderWindowSizeTest.cpp mitkMultiComponentImageDataComparisonFilterTest.cpp mitkImageToItkTest.cpp mitkImageSliceSelectorTest.cpp ) # Currently not working on windows because of a rendering timing issue # see bug 18083 for details if(NOT WIN32) set(MODULE_CUSTOM_TESTS ${MODULE_CUSTOM_TESTS} mitkSurfaceDepthSortingTest.cpp) endif() set(RESOURCE_FILES Interactions/AddAndRemovePoints.xml Interactions/globalConfig.xml Interactions/StatemachineTest.xml Interactions/StatemachineConfigTest.xml ) diff --git a/Modules/Core/test/mitkLevelWindowManagerTest.cpp b/Modules/Core/test/mitkLevelWindowManagerTest.cpp index 9aaf1f8f22..11902e1ac3 100644 --- a/Modules/Core/test/mitkLevelWindowManagerTest.cpp +++ b/Modules/Core/test/mitkLevelWindowManagerTest.cpp @@ -1,625 +1,625 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "itkMersenneTwisterRandomVariateGenerator.h" #include "mitkLevelWindowManager.h" #include "mitkRenderingModeProperty.h" #include "mitkStandaloneDataStorage.h" #include #include #include #include #include #include #include #include class mitkLevelWindowManagerTestClass { public: static void TestInstantiation() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); MITK_TEST_CONDITION_REQUIRED(manager.IsNotNull(), "Testing mitk::LevelWindowManager::New()"); } static void TestSetGetDataStorage() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); MITK_TEST_OUTPUT(<< "Creating DataStorage: "); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); bool success = true; try { manager->SetDataStorage(ds); } catch (std::exception &e) { success = false; MITK_ERROR << "Exception: " << e.what(); } MITK_TEST_CONDITION_REQUIRED(success, "Testing mitk::LevelWindowManager SetDataStorage() "); MITK_TEST_CONDITION_REQUIRED(ds == manager->GetDataStorage(), "Testing mitk::LevelWindowManager GetDataStorage "); } static void TestMethodsWithInvalidParameters() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); bool success = false; mitk::LevelWindowProperty::Pointer levelWindowProperty = mitk::LevelWindowProperty::New(); try { manager->SetLevelWindowProperty(levelWindowProperty); } catch (const mitk::Exception &) { success = true; } MITK_TEST_CONDITION(success, "Testing mitk::LevelWindowManager SetLevelWindowProperty with invalid parameter"); } static void TestOtherMethods() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); - MITK_TEST_CONDITION(manager->isAutoTopMost(), "Testing mitk::LevelWindowManager isAutoTopMost"); + MITK_TEST_CONDITION(manager->IsAutoTopMost(), "Testing mitk::LevelWindowManager isAutoTopMost"); // It is not clear what the following code is supposed to test. The expression in // the catch(...) block does have no effect, so success is always true. // Related bugs are 13894 and 13889 /* bool success = true; try { mitk::LevelWindow levelWindow = manager->GetLevelWindow(); manager->SetLevelWindow(levelWindow); } catch (...) { success == false; } MITK_TEST_CONDITION(success,"Testing mitk::LevelWindowManager GetLevelWindow() and SetLevelWindow()"); */ manager->SetAutoTopMostImage(true); - MITK_TEST_CONDITION(manager->isAutoTopMost(), "Testing mitk::LevelWindowManager isAutoTopMost()"); + MITK_TEST_CONDITION(manager->IsAutoTopMost(), "Testing mitk::LevelWindowManager isAutoTopMost()"); } static void TestRemoveObserver(std::string testImageFile) { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); // add multiple objects to the data storage => multiple observers should be created mitk::Image::Pointer image1 = mitk::IOUtil::Load(testImageFile); mitk::DataNode::Pointer node1 = mitk::DataNode::New(); node1->SetData(image1); mitk::Image::Pointer image2 = mitk::IOUtil::Load(testImageFile); mitk::DataNode::Pointer node2 = mitk::DataNode::New(); node2->SetData(image2); ds->Add(node1); ds->Add(node2); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 2, "Test if nodes have been added"); MITK_TEST_CONDITION_REQUIRED( static_cast(manager->GetRelevantNodes()->size()) == manager->GetNumberOfObservers(), "Test if number of nodes is similar to number of observers"); mitk::Image::Pointer image3 = mitk::IOUtil::Load(testImageFile); mitk::DataNode::Pointer node3 = mitk::DataNode::New(); node3->SetData(image3); ds->Add(node3); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 3, "Test if another node have been added"); MITK_TEST_CONDITION_REQUIRED( static_cast(manager->GetRelevantNodes()->size()) == manager->GetNumberOfObservers(), "Test if number of nodes is similar to number of observers"); ds->Remove(node1); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 2, "Deleted node 1 (test GetRelevantNodes())"); MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 2, "Deleted node 1 (test GetNumberOfObservers())"); ds->Remove(node2); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 1, "Deleted node 2 (test GetRelevantNodes())"); MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 1, "Deleted node 2 (test GetNumberOfObservers())"); ds->Remove(node3); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 0, "Deleted node 3 (test GetRelevantNodes())"); MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 0, "Deleted node 3 (test GetNumberOfObservers())"); } static bool VerifyRenderingModes() { bool ok = (mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR == 1) && (mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR == 2) && (mitk::RenderingModeProperty::LOOKUPTABLE_COLOR == 3) && (mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR == 4); return ok; } static void TestLevelWindowSliderVisibility(std::string testImageFile) { bool renderingModesValid = mitkLevelWindowManagerTestClass::VerifyRenderingModes(); if (!renderingModesValid) { MITK_ERROR << "Exception: Image Rendering.Mode property value types inconsistent."; } mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); // add multiple objects to the data storage => multiple observers should be created mitk::Image::Pointer image1 = mitk::IOUtil::Load(testImageFile); mitk::DataNode::Pointer node1 = mitk::DataNode::New(); node1->SetData(image1); ds->Add(node1); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); std::vector nodeVec; // nodeVec.resize( 3 ); nodeVec.push_back(node1); nodeVec.push_back(node2); nodeVec.push_back(node3); typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; RandomGeneratorType::Pointer rnd = RandomGeneratorType::New(); rnd->Initialize(); for (unsigned int i = 0; i < 8; ++i) { unsigned int parity = i; for (unsigned int img = 0; img < 3; ++img) { if (parity & 1) { int mode = rnd->GetIntegerVariate() % 3; nodeVec[img]->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New(mode)); } else { int mode = rnd->GetIntegerVariate() % 2; nodeVec[img]->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New(3 + mode)); } parity >>= 1; } MITK_TEST_CONDITION( renderingModesValid && ((!manager->GetLevelWindowProperty() && !i) || (manager->GetLevelWindowProperty() && i)), "Testing level window property member according to rendering mode"); } } static void TestSetLevelWindowProperty(std::string testImageFile) { mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); // add multiple objects to the data storage => multiple observers should be created mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); node3->SetIntProperty("layer", 1); node2->SetIntProperty("layer", 2); node1->SetIntProperty("layer", 3); manager->SetAutoTopMostImage(true); bool isImageForLevelWindow1, isImageForLevelWindow2, isImageForLevelWindow3; node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 1."); manager->SetAutoTopMostImage(false); mitk::LevelWindowProperty::Pointer prop = dynamic_cast(node2->GetProperty("levelwindow")); manager->SetLevelWindowProperty(prop); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(!isImageForLevelWindow1 && isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 2."); prop = dynamic_cast(node3->GetProperty("levelwindow")); manager->SetLevelWindowProperty(prop); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(!isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); prop = dynamic_cast(node1->GetProperty("levelwindow")); manager->SetLevelWindowProperty(prop); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); } static void TestImageForLevelWindowOnVisibilityChange(std::string testImageFile) { mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); // add multiple objects to the data storage => multiple observers should be created mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); node3->SetIntProperty("layer", 1); node2->SetIntProperty("layer", 2); node1->SetIntProperty("layer", 3); manager->SetAutoTopMostImage(false); bool isImageForLevelWindow1, isImageForLevelWindow2, isImageForLevelWindow3; node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing initial imageForLevelWindow setting."); node1->SetVisibility(false); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(!isImageForLevelWindow1 && isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 2."); node2->SetVisibility(false); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(!isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); node3->SetVisibility(false); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(!isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); node1->SetVisibility(true); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); } static void TestImageForLevelWindowOnRandomPropertyChange(std::string testImageFile) { typedef std::vector BoolVecType; typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; // initialize the data storage mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); node3->SetIntProperty("layer", 1); node2->SetIntProperty("layer", 2); node1->SetIntProperty("layer", 3); // node visibilities std::vector nodesVisible; nodesVisible.resize(3); std::fill(nodesVisible.begin(), nodesVisible.end(), true); // which node has the level window std::vector nodesForLevelWindow; nodesForLevelWindow.resize(3); std::fill(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), false); // the nodes themselves std::vector nodes; nodes.push_back(node1); nodes.push_back(node2); nodes.push_back(node3); // status quo manager->SetAutoTopMostImage(false); bool lvlWin1, lvlWin2, lvlWin3; node1->GetBoolProperty("imageForLevelWindow", lvlWin1); node2->GetBoolProperty("imageForLevelWindow", lvlWin2); node3->GetBoolProperty("imageForLevelWindow", lvlWin3); MITK_TEST_CONDITION(lvlWin1 && !lvlWin2 && !lvlWin3, "Testing initial imageForLevelWindow setting."); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; // prepare randomized visibility changes RandomGeneratorType::Pointer ranGen = RandomGeneratorType::New(); ranGen->Initialize(); int ranCount = 100; int validCount = 0; int invalidCount = 0; int mustHaveLvlWindow = 4; for (int run = 0; run < ranCount; ++run) { // toggle node visibility int ran = ranGen->GetIntegerVariate(2); nodes[ran]->SetBoolProperty("imageForLevelWindow", !nodesForLevelWindow[ran]); // one node must have the level window std::vector::const_iterator found = std::find(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), true); if (found == nodesForLevelWindow.end()) { break; } // all invisible? found = std::find(nodesVisible.begin(), nodesVisible.end(), true); if (!nodesForLevelWindow[ran]) { mustHaveLvlWindow = pow(2, 2 - ran); } else { mustHaveLvlWindow = 4; } // get the current status node1->GetBoolProperty("imageForLevelWindow", lvlWin1); node2->GetBoolProperty("imageForLevelWindow", lvlWin2); node3->GetBoolProperty("imageForLevelWindow", lvlWin3); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; int hasLevelWindow = 0; for (int i = 0; i < 3; ++i) { if (nodesForLevelWindow[i]) { hasLevelWindow += pow(2, 2 - i); } } validCount += hasLevelWindow == mustHaveLvlWindow ? 1 : 0; // test sensitivity int falseran = 0; while (falseran == 0) { falseran = ranGen->GetIntegerVariate(7); } BoolVecType falseNodes; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin1 : lvlWin1); falseran >>= 1; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin2 : lvlWin2); falseran >>= 1; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin3 : lvlWin3); int falseLevelWindow = 0; for (int i = 0; i < 3; ++i) { if (falseNodes[i]) { falseLevelWindow += pow(2, 2 - i); } } invalidCount += falseLevelWindow == mustHaveLvlWindow ? 0 : 1; // in case of errors proceed anyway mustHaveLvlWindow = hasLevelWindow; } MITK_TEST_CONDITION(validCount == ranCount, "Testing proper node for level window property."); MITK_TEST_CONDITION(invalidCount == ranCount, "Sensitivity test."); } static void TestImageForLevelWindowOnRandomVisibilityChange(std::string testImageFile) { typedef std::vector BoolVecType; typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; // initialize the data storage mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); node3->SetIntProperty("layer", 1); node2->SetIntProperty("layer", 2); node1->SetIntProperty("layer", 3); // node visibilities std::vector nodesVisible; nodesVisible.resize(3); std::fill(nodesVisible.begin(), nodesVisible.end(), true); // which node has the level window std::vector nodesForLevelWindow; nodesForLevelWindow.resize(3); std::fill(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), false); // the nodes themselves std::vector nodes; nodes.push_back(node1); nodes.push_back(node2); nodes.push_back(node3); // status quo manager->SetAutoTopMostImage(false); bool lvlWin1, lvlWin2, lvlWin3; node1->GetBoolProperty("imageForLevelWindow", lvlWin1); node2->GetBoolProperty("imageForLevelWindow", lvlWin2); node3->GetBoolProperty("imageForLevelWindow", lvlWin3); MITK_TEST_CONDITION(lvlWin1 && !lvlWin2 && !lvlWin3, "Testing initial imageForLevelWindow setting."); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; // prepare randomized visibility changes RandomGeneratorType::Pointer ranGen = RandomGeneratorType::New(); ranGen->Initialize(); int ranCount = 100; int validCount = 0; int invalidCount = 0; int mustHaveLvlWindow = 4; for (int run = 0; run < ranCount; ++run) { // toggle node visibility int ran = ranGen->GetIntegerVariate(2); nodesVisible[ran] = !nodesVisible[ran]; nodes[ran]->SetVisibility(nodesVisible[ran]); // one node must have the level window std::vector::const_iterator found = std::find(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), true); if (found == nodesForLevelWindow.end()) { break; } int ind = found - nodesForLevelWindow.begin(); // all invisible? found = std::find(nodesVisible.begin(), nodesVisible.end(), true); bool allInvisible = (found == nodesVisible.end()); // which node shall get the level window now if (!allInvisible && !nodesVisible[ind]) { int count = 0; for (std::vector::const_iterator it = nodesVisible.begin(); it != nodesVisible.end(); ++it, ++count) { if (*it) { mustHaveLvlWindow = pow(2, 2 - count); break; } } } // get the current status node1->GetBoolProperty("imageForLevelWindow", lvlWin1); node2->GetBoolProperty("imageForLevelWindow", lvlWin2); node3->GetBoolProperty("imageForLevelWindow", lvlWin3); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; int hasLevelWindow = 0; for (int i = 0; i < 3; ++i) { if (nodesForLevelWindow[i]) { hasLevelWindow += pow(2, 2 - i); } } validCount += hasLevelWindow == mustHaveLvlWindow ? 1 : 0; // test sensitivity int falseran = 0; while (falseran == 0) { falseran = ranGen->GetIntegerVariate(7); } BoolVecType falseNodes; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin1 : lvlWin1); falseran >>= 1; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin2 : lvlWin2); falseran >>= 1; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin3 : lvlWin3); int falseLevelWindow = 0; for (int i = 0; i < 3; ++i) { if (falseNodes[i]) { falseLevelWindow += pow(2, 2 - i); } } invalidCount += falseLevelWindow == mustHaveLvlWindow ? 0 : 1; // in case of errors proceed anyway mustHaveLvlWindow = hasLevelWindow; } MITK_TEST_CONDITION(validCount == ranCount, "Testing proper node for level window property."); MITK_TEST_CONDITION(invalidCount == ranCount, "Sensitivity test."); } }; int mitkLevelWindowManagerTest(int argc, char *args[]) { MITK_TEST_BEGIN("mitkLevelWindowManager"); MITK_TEST_CONDITION_REQUIRED(argc >= 2, "Testing if test file is given."); std::string testImage = args[1]; mitkLevelWindowManagerTestClass::TestInstantiation(); mitkLevelWindowManagerTestClass::TestSetGetDataStorage(); mitkLevelWindowManagerTestClass::TestMethodsWithInvalidParameters(); mitkLevelWindowManagerTestClass::TestOtherMethods(); mitkLevelWindowManagerTestClass::TestRemoveObserver(testImage); mitkLevelWindowManagerTestClass::TestLevelWindowSliderVisibility(testImage); mitkLevelWindowManagerTestClass::TestSetLevelWindowProperty(testImage); mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnVisibilityChange(testImage); mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnRandomVisibilityChange(testImage); mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnRandomPropertyChange(testImage); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkPlaneGeometryTest.cpp b/Modules/Core/test/mitkPlaneGeometryTest.cpp index ba4a70cbff..10ea8ad159 100644 --- a/Modules/Core/test/mitkPlaneGeometryTest.cpp +++ b/Modules/Core/test/mitkPlaneGeometryTest.cpp @@ -1,1078 +1,1097 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkAffineTransform3D.h" #include "mitkBaseGeometry.h" #include "mitkGeometry3D.h" #include "mitkInteractionConst.h" #include "mitkLine.h" #include "mitkPlaneGeometry.h" #include "mitkRotationOperation.h" #include "mitkSlicedGeometry3D.h" #include "mitkThinPlateSplineCurvedGeometry.h" #include #include #include #include #include #include +#include +#include static const mitk::ScalarType testEps = 1E-9; // the epsilon used in this test == at least float precision. class mitkPlaneGeometryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPlaneGeometryTestSuite); MITK_TEST(TestInitializeStandardPlane); MITK_TEST(TestProjectPointOntoPlane); MITK_TEST(TestPlaneGeometryCloning); MITK_TEST(TestInheritance); MITK_TEST(TestSetExtendInMM); MITK_TEST(TestRotate); MITK_TEST(TestClone); MITK_TEST(TestPlaneComparison); MITK_TEST(TestAxialInitialization); MITK_TEST(TestFrontalInitialization); MITK_TEST(TestSaggitalInitialization); MITK_TEST(TestLefthandedCoordinateSystem); + MITK_TEST(TestDominantAxesError); + MITK_TEST(TestCheckRotationMatrix); // Currently commented out, see See bug 15990 // MITK_TEST(testPlaneGeometryInitializeOrder); MITK_TEST(TestIntersectionPoint); MITK_TEST(TestCase1210); CPPUNIT_TEST_SUITE_END(); private: // private test members that are initialized by setUp() mitk::PlaneGeometry::Pointer planegeometry; mitk::Point3D origin; mitk::Vector3D right, bottom, normal, spacing; mitk::ScalarType width, height; mitk::ScalarType widthInMM, heightInMM, thicknessInMM; public: void setUp() override { planegeometry = mitk::PlaneGeometry::New(); width = 100; widthInMM = width; height = 200; heightInMM = height; thicknessInMM = 1.0; mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); mitk::FillVector3D(normal, 0, 0, thicknessInMM); mitk::FillVector3D(spacing, 1.0, 1.0, thicknessInMM); planegeometry->InitializeStandardPlane(right, bottom); planegeometry->SetOrigin(origin); planegeometry->SetSpacing(spacing); } void tearDown() override {} // This test verifies inheritance behaviour, this test will fail if the behaviour changes in the future void TestInheritance() { mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); mitk::Geometry3D::Pointer g3d = dynamic_cast(plane.GetPointer()); CPPUNIT_ASSERT_MESSAGE("Planegeometry should not be castable to Geometry 3D", g3d.IsNull()); mitk::BaseGeometry::Pointer base = dynamic_cast(plane.GetPointer()); CPPUNIT_ASSERT_MESSAGE("Planegeometry should be castable to BaseGeometry", base.IsNotNull()); g3d = mitk::Geometry3D::New(); base = dynamic_cast(g3d.GetPointer()); CPPUNIT_ASSERT_MESSAGE("Geometry3D should be castable to BaseGeometry", base.IsNotNull()); mitk::SlicedGeometry3D::Pointer sliced = mitk::SlicedGeometry3D::New(); g3d = dynamic_cast(sliced.GetPointer()); CPPUNIT_ASSERT_MESSAGE("SlicedGeometry3D should not be castable to Geometry3D", g3d.IsNull()); mitk::ThinPlateSplineCurvedGeometry::Pointer thin = mitk::ThinPlateSplineCurvedGeometry::New(); plane = dynamic_cast(thin.GetPointer()); CPPUNIT_ASSERT_MESSAGE("AbstractTransformGeometry should be castable to PlaneGeometry", plane.IsNotNull()); plane = mitk::PlaneGeometry::New(); mitk::AbstractTransformGeometry::Pointer atg = dynamic_cast(plane.GetPointer()); CPPUNIT_ASSERT_MESSAGE("PlaneGeometry should not be castable to AbstractTransofrmGeometry", atg.IsNull()); } + void TestDominantAxesError() + { + auto image = mitk::IOUtil::Load(GetTestDataFilePath("NotQuiteARotationMatrix.nrrd")); + auto matrix = image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().transpose(); + std::vector< int > axes = mitk::PlaneGeometry::CalculateDominantAxes(matrix); + CPPUNIT_ASSERT_MESSAGE("Domiant axes cannot be determined in this dataset. Output should be default ordering.", axes.at(0)==0 && axes.at(1)==1 && axes.at(2)==2); + } + + void TestCheckRotationMatrix() + { + auto image = mitk::IOUtil::Load(GetTestDataFilePath("NotQuiteARotationMatrix.nrrd")); + bool is_rotation = mitk::PlaneGeometry::CheckRotationMatrix(image->GetGeometry()->GetIndexToWorldTransform()); + CPPUNIT_ASSERT_MESSAGE("Since the test data matrix is not quite a rotation matrix, this should be detected.", !is_rotation); + } + void TestLefthandedCoordinateSystem() { /** * @brief This method tests InitializeStandardPlane() and IndexToWorld() * with a left-handed coordinate orientation or indexToWorldMatrix. * * Of course this test does not use standard Parameters, which are right-handed. * See also discussion of bug #11477: http://bugs.mitk.org/show_bug.cgi?id=11477 */ planegeometry = mitk::PlaneGeometry::New(); width = 100; widthInMM = 5; height = 200; heightInMM = 3; thicknessInMM = 1.0; mitk::FillVector3D(right, widthInMM, 0, 0); mitk::FillVector3D(bottom, 0, heightInMM, 0); // This one negative sign results in lefthanded coordinate orientation and det(matrix) < 0. mitk::FillVector3D(normal, 0, 0, -thicknessInMM); mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix; mitk::AffineTransform3D::MatrixType::InternalMatrixType &vnl_matrix = matrix.GetVnlMatrix(); vnl_matrix.set_column(0, right); vnl_matrix.set_column(1, bottom); vnl_matrix.set_column(2, normal); // making sure that we didn't screw up this special test case or else fail deadly: assert(vnl_determinant(vnl_matrix) < 0.0); transform->SetIdentity(); transform->SetMatrix(matrix); planegeometry->InitializeStandardPlane(width, height, transform); // Crux of the matter. CPPUNIT_ASSERT_MESSAGE( "Testing if IndexToWorldMatrix is correct after InitializeStandardPlane( width, height, transform ) ", mitk::MatrixEqualElementWise(planegeometry->GetIndexToWorldTransform()->GetMatrix(), matrix)); mitk::Point3D p_index; p_index[0] = 10.; p_index[1] = 10.; p_index[2] = 0.; mitk::Point3D p_world; mitk::Point3D p_expectedResult; p_expectedResult[0] = 50.; p_expectedResult[1] = 30.; p_expectedResult[2] = 0.; ((mitk::BaseGeometry::Pointer)planegeometry)->IndexToWorld(p_index, p_world); // Crux of the matter. CPPUNIT_ASSERT_MESSAGE("Testing if IndexToWorld(a,b) function works correctly with lefthanded matrix ", mitk::Equal(p_world, p_expectedResult, testEps)); } // See bug 1210 // Test does not use standard Parameters void TestCase1210() { mitk::PlaneGeometry::Pointer planegeometry = mitk::PlaneGeometry::New(); mitk::Point3D origin; mitk::Vector3D right, down, spacing; mitk::FillVector3D(origin, 4.5, 7.3, 11.2); mitk::FillVector3D(right, 1.015625, 1.015625, 1.1999969482421875); mitk::FillVector3D(down, 1.4012984643248170709237295832899161312802619418765e-45, 0, 0); mitk::FillVector3D(spacing, 0, 1.4713633875410579244699160624544119378442750389703e-43, 9.2806360452222355258639080851310540729807238879469e-32); std::cout << "Testing InitializeStandardPlane(rightVector, downVector, spacing = nullptr): " << std::endl; CPPUNIT_ASSERT_NO_THROW(planegeometry->InitializeStandardPlane(right, down, &spacing)); /* std::cout << "Testing width, height and thickness (in units): "; if((mitk::Equal(planegeometry->GetExtent(0),width)==false) || (mitk::Equal(planegeometry->GetExtent(1),height)==false) || (mitk::Equal(planegeometry->GetExtent(2),1)==false) ) { std::cout<<"[FAILED]"<GetExtentInMM(0),widthInMM)==false) || (mitk::Equal(planegeometry->GetExtentInMM(1),heightInMM)==false) || (mitk::Equal(planegeometry->GetExtentInMM(2),thicknessInMM)==false) ) { std::cout<<"[FAILED]"< 0. * */ // Test does not use standard Parameters void TestIntersectionPoint() { // init plane with its parameter mitk::PlaneGeometry::Pointer myPlaneGeometry = mitk::PlaneGeometry::New(); mitk::Point3D origin; origin[0] = 0.0; origin[1] = 2.0; origin[2] = 0.0; mitk::Vector3D normal; normal[0] = 0.0; normal[1] = 1.0; normal[2] = 0.0; myPlaneGeometry->InitializePlane(origin, normal); // generate points and line for intersection testing // point distance of given line > 1 mitk::Point3D pointP1; pointP1[0] = 2.0; pointP1[1] = 1.0; pointP1[2] = 0.0; mitk::Point3D pointP2; pointP2[0] = 2.0; pointP2[1] = 4.0; pointP2[2] = 0.0; mitk::Vector3D lineDirection; lineDirection[0] = pointP2[0] - pointP1[0]; lineDirection[1] = pointP2[1] - pointP1[1]; lineDirection[2] = pointP2[2] - pointP1[2]; mitk::Line3D xingline(pointP1, lineDirection); mitk::Point3D calcXingPoint; myPlaneGeometry->IntersectionPoint(xingline, calcXingPoint); // point distance of given line < 1 mitk::Point3D pointP3; pointP3[0] = 2.0; pointP3[1] = 2.2; pointP3[2] = 0.0; mitk::Point3D pointP4; pointP4[0] = 2.0; pointP4[1] = 1.7; pointP4[2] = 0.0; mitk::Vector3D lineDirection2; lineDirection2[0] = pointP4[0] - pointP3[0]; lineDirection2[1] = pointP4[1] - pointP3[1]; lineDirection2[2] = pointP4[2] - pointP3[2]; mitk::Line3D xingline2(pointP3, lineDirection2); mitk::Point3D calcXingPoint2; myPlaneGeometry->IntersectionPoint(xingline2, calcXingPoint2); // intersection points must be the same CPPUNIT_ASSERT_MESSAGE("Failed to calculate Intersection Point", calcXingPoint == calcXingPoint2); } /** * @brief This method tests method ProjectPointOntoPlane. * * See also bug #3409. */ // Test does not use standard Parameters void TestProjectPointOntoPlane() { mitk::PlaneGeometry::Pointer myPlaneGeometry = mitk::PlaneGeometry::New(); // create normal mitk::Vector3D normal; normal[0] = 0.0; normal[1] = 0.0; normal[2] = 1.0; // create origin mitk::Point3D origin; origin[0] = -27.582859; origin[1] = 50; origin[2] = 200.27742; // initialize plane geometry myPlaneGeometry->InitializePlane(origin, normal); // output to descripe the test std::cout << "Testing PlaneGeometry according to bug #3409" << std::endl; std::cout << "Our normal is: " << normal << std::endl; std::cout << "So ALL projected points should have exactly the same z-value!" << std::endl; // create a number of points mitk::Point3D myPoints[5]; myPoints[0][0] = -27.582859; myPoints[0][1] = 50.00; myPoints[0][2] = 200.27742; myPoints[1][0] = -26.58662; myPoints[1][1] = 50.00; myPoints[1][2] = 200.19026; myPoints[2][0] = -26.58662; myPoints[2][1] = 50.00; myPoints[2][2] = 200.33124; myPoints[3][0] = 104.58662; myPoints[3][1] = 452.12313; myPoints[3][2] = 866.41236; myPoints[4][0] = -207.58662; myPoints[4][1] = 312.00; myPoints[4][2] = -300.12346; // project points onto plane mitk::Point3D myProjectedPoints[5]; for (unsigned int i = 0; i < 5; ++i) { myProjectedPoints[i] = myPlaneGeometry->ProjectPointOntoPlane(myPoints[i]); } // compare z-values with z-value of plane (should be equal) bool allPointsOnPlane = true; for (auto &myProjectedPoint : myProjectedPoints) { if (fabs(myProjectedPoint[2] - origin[2]) > mitk::sqrteps) { allPointsOnPlane = false; } } CPPUNIT_ASSERT_MESSAGE("All points lie not on the same plane", allPointsOnPlane); } void TestPlaneGeometryCloning() { mitk::PlaneGeometry::Pointer geometry2D = createPlaneGeometry(); try { mitk::PlaneGeometry::Pointer clone = geometry2D->Clone(); itk::Matrix matrix = clone->GetIndexToWorldTransform()->GetMatrix(); CPPUNIT_ASSERT_MESSAGE("Test if matrix element exists...", matrix[0][0] == 31); double origin = geometry2D->GetOrigin()[0]; CPPUNIT_ASSERT_MESSAGE("First Point of origin as expected...", mitk::Equal(origin, 8)); double spacing = geometry2D->GetSpacing()[0]; CPPUNIT_ASSERT_MESSAGE("First Point of spacing as expected...", mitk::Equal(spacing, 31)); } catch (...) { CPPUNIT_FAIL("Error during access on a member of cloned geometry"); } // direction [row] [coloum] MITK_TEST_OUTPUT(<< "Casting a rotated 2D ITK Image to a MITK Image and check if Geometry is still same"); } void TestPlaneGeometryInitializeOrder() { mitk::Vector3D mySpacing; mySpacing[0] = 31; mySpacing[1] = 0.1; mySpacing[2] = 5.4; mitk::Point3D myOrigin; myOrigin[0] = 8; myOrigin[1] = 9; myOrigin[2] = 10; mitk::AffineTransform3D::Pointer myTransform = mitk::AffineTransform3D::New(); itk::Matrix transMatrix; transMatrix.Fill(0); transMatrix[0][0] = 1; transMatrix[1][1] = 2; transMatrix[2][2] = 4; myTransform->SetMatrix(transMatrix); mitk::PlaneGeometry::Pointer geometry2D1 = mitk::PlaneGeometry::New(); geometry2D1->SetIndexToWorldTransform(myTransform); geometry2D1->SetSpacing(mySpacing); geometry2D1->SetOrigin(myOrigin); mitk::PlaneGeometry::Pointer geometry2D2 = mitk::PlaneGeometry::New(); geometry2D2->SetSpacing(mySpacing); geometry2D2->SetOrigin(myOrigin); geometry2D2->SetIndexToWorldTransform(myTransform); mitk::PlaneGeometry::Pointer geometry2D3 = mitk::PlaneGeometry::New(); geometry2D3->SetIndexToWorldTransform(myTransform); geometry2D3->SetSpacing(mySpacing); geometry2D3->SetOrigin(myOrigin); geometry2D3->SetIndexToWorldTransform(myTransform); CPPUNIT_ASSERT_MESSAGE("Origin of Geometry 1 matches that of Geometry 2.", mitk::Equal(geometry2D1->GetOrigin(), geometry2D2->GetOrigin())); CPPUNIT_ASSERT_MESSAGE("Origin of Geometry 1 match those of Geometry 3.", mitk::Equal(geometry2D1->GetOrigin(), geometry2D3->GetOrigin())); CPPUNIT_ASSERT_MESSAGE("Origin of Geometry 2 match those of Geometry 3.", mitk::Equal(geometry2D2->GetOrigin(), geometry2D3->GetOrigin())); CPPUNIT_ASSERT_MESSAGE("Spacing of Geometry 1 match those of Geometry 2.", mitk::Equal(geometry2D1->GetSpacing(), geometry2D2->GetSpacing())); CPPUNIT_ASSERT_MESSAGE("Spacing of Geometry 1 match those of Geometry 3.", mitk::Equal(geometry2D1->GetSpacing(), geometry2D3->GetSpacing())); CPPUNIT_ASSERT_MESSAGE("Spacing of Geometry 2 match those of Geometry 3.", mitk::Equal(geometry2D2->GetSpacing(), geometry2D3->GetSpacing())); CPPUNIT_ASSERT_MESSAGE("Transformation of Geometry 1 match those of Geometry 2.", compareMatrix(geometry2D1->GetIndexToWorldTransform()->GetMatrix(), geometry2D2->GetIndexToWorldTransform()->GetMatrix())); CPPUNIT_ASSERT_MESSAGE("Transformation of Geometry 1 match those of Geometry 3.", compareMatrix(geometry2D1->GetIndexToWorldTransform()->GetMatrix(), geometry2D3->GetIndexToWorldTransform()->GetMatrix())); CPPUNIT_ASSERT_MESSAGE("Transformation of Geometry 2 match those of Geometry 3.", compareMatrix(geometry2D2->GetIndexToWorldTransform()->GetMatrix(), geometry2D3->GetIndexToWorldTransform()->GetMatrix())); } void TestInitializeStandardPlane() { CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: width", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: height", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: depth", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: width in mm", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: heght in mm", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: depth in mm", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: AxisVectorRight", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: AxisVectorBottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with default Spacing: AxisVectorNormal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); mitk::Vector3D spacing; thicknessInMM = 1.5; normal.Normalize(); normal *= thicknessInMM; mitk::FillVector3D(spacing, 1.0, 1.0, thicknessInMM); planegeometry->InitializeStandardPlane(right.GetVnlVector(), bottom.GetVnlVector(), &spacing); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: width", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: height", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: depth", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: width in mm", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: height in mm", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: depth in mm", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: AxisVectorRight", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: AxisVectorBottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing correct Standard Plane initialization with custom Spacing: AxisVectorNormal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); ; } void TestSetExtendInMM() { normal.Normalize(); normal *= thicknessInMM; planegeometry->SetExtentInMM(2, thicknessInMM); CPPUNIT_ASSERT_MESSAGE("Testing SetExtentInMM(2, ...), querying by GetExtentInMM(2): ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing SetExtentInMM(2, ...), querying by GetAxisVector(2) and comparing to normal: ", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); planegeometry->SetOrigin(origin); CPPUNIT_ASSERT_MESSAGE("Testing SetOrigin", mitk::Equal(planegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() after SetOrigin: Right", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() after SetOrigin: Bottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() after SetOrigin: Normal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestRotate() { // Changing the IndexToWorldTransform to a rotated version by SetIndexToWorldTransform() (keep origin): mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = planegeometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::VnlVector axis(3); mitk::FillVector3D(axis, 1.0, 1.0, 1.0); axis.normalize(); vnl_quaternion rotation(axis, 0.223); vnlmatrix = rotation.rotation_matrix_transpose() * vnlmatrix; mitk::Matrix3D matrix; matrix = vnlmatrix; transform->SetMatrix(matrix); transform->SetOffset(planegeometry->GetIndexToWorldTransform()->GetOffset()); right.SetVnlVector(rotation.rotation_matrix_transpose() * right.GetVnlVector()); bottom.SetVnlVector(rotation.rotation_matrix_transpose() * bottom.GetVnlVector()); normal.SetVnlVector(rotation.rotation_matrix_transpose() * normal.GetVnlVector()); planegeometry->SetIndexToWorldTransform(transform); // The origin changed,because m_Origin=m_IndexToWorldTransform->GetOffset()+GetAxisVector(2)*0.5 // and the AxisVector changes due to the rotation. In other words: the rotation was done around // the corner of the box, not around the planes origin. Now change it to a rotation around // the origin, simply by re-setting the origin to the original one: planegeometry->SetOrigin(origin); CPPUNIT_ASSERT_MESSAGE("Testing whether SetIndexToWorldTransform kept origin: ", mitk::Equal(planegeometry->GetOrigin(), origin, testEps)); mitk::Point2D point; point[0] = 4; point[1] = 3; mitk::Point2D dummy; planegeometry->WorldToIndex(point, dummy); planegeometry->IndexToWorld(dummy, dummy); CPPUNIT_ASSERT_MESSAGE("Testing consistency of index and world coordinates.", dummy == point); CPPUNIT_ASSERT_MESSAGE("Testing width of rotated version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height of rotated version: ", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness of rotated version: ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of rotated version: right ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of rotated version: bottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of rotated version: normal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(0).GetNorm(), planegeometry->GetExtentInMM(0), testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(1).GetNorm(), planegeometry->GetExtentInMM(1), testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(2).GetNorm(), planegeometry->GetExtentInMM(2), testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); width *= 2; height *= 3; planegeometry->SetSizeInUnits(width, height); CPPUNIT_ASSERT_MESSAGE("Testing SetSizeInUnits() of rotated version: ", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing SetSizeInUnits() of rotated version: ", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing SetSizeInUnits() of rotated version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of version with changed size in units: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of version with changed size in units: ", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of version with changed size in units: ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of version with changed size in units: right ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of version with changed size in units: bottom", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of version with changed size in units: normal", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(0).GetNorm(), planegeometry->GetExtentInMM(0), testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(1).GetNorm(), planegeometry->GetExtentInMM(1), testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing GetAxisVector(direction).GetNorm() != planegeometry->GetExtentInMM(direction) of rotated version: ", mitk::Equal(planegeometry->GetAxisVector(2).GetNorm(), planegeometry->GetExtentInMM(2), testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestClone() { mitk::PlaneGeometry::Pointer clonedplanegeometry = dynamic_cast(planegeometry->Clone().GetPointer()); // Cave: Statement below is negated! CPPUNIT_ASSERT_MESSAGE("Testing Clone(): ", !((clonedplanegeometry.IsNull()) || (clonedplanegeometry->GetReferenceCount() != 1))); CPPUNIT_ASSERT_MESSAGE("Testing origin of cloned version: ", mitk::Equal(clonedplanegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in units) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in units) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing extent (in units) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of cloned version: ", mitk::Equal(clonedplanegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of cloned version: right", mitk::Equal(clonedplanegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of cloned version: bottom", mitk::Equal(clonedplanegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of cloned version: normal", mitk::Equal(clonedplanegeometry->GetAxisVector(2), normal, testEps)); mappingTests2D(clonedplanegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestSaggitalInitialization() { mitk::Point3D cornerpoint0 = planegeometry->GetCornerPoint(0); mitk::PlaneGeometry::Pointer clonedplanegeometry = planegeometry->Clone(); // Testing InitializeStandardPlane(clonedplanegeometry, planeorientation = Sagittal, zPosition = 0, frontside=true): planegeometry->InitializeStandardPlane(clonedplanegeometry, mitk::PlaneGeometry::Sagittal); mitk::Vector3D newright, newbottom, newnormal; mitk::ScalarType newthicknessInMM; newright = bottom; newthicknessInMM = widthInMM / width * 1.0; // extent in normal direction is 1; newnormal = right; newnormal.Normalize(); newnormal *= newthicknessInMM; newbottom = normal; newbottom.Normalize(); newbottom *= thicknessInMM; CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of sagitally initialized version:", mitk::Equal(planegeometry->GetCornerPoint(0), cornerpoint0, testEps)); // ok, corner was fine, so we can dare to believe the origin is ok. origin = planegeometry->GetOrigin(); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of sagitally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), newthicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of sagitally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of sagitally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of sagitally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), newnormal, testEps)); mappingTests2D(planegeometry, height, 1, heightInMM, thicknessInMM, origin, newright, newbottom); // set origin back to the one of the axial slice: origin = clonedplanegeometry->GetOrigin(); // Testing backside initialization: InitializeStandardPlane(clonedplanegeometry, planeorientation = Axial, zPosition // = 0, frontside=false, rotated=true): planegeometry->InitializeStandardPlane(clonedplanegeometry, mitk::PlaneGeometry::Axial, 0, false, true); mitk::Point3D backsideorigin; backsideorigin = origin + clonedplanegeometry->GetAxisVector(1); //+clonedplanegeometry->GetAxisVector(2); CPPUNIT_ASSERT_MESSAGE("Testing origin of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetOrigin(), backsideorigin, testEps)); mitk::Point3D backsidecornerpoint0; backsidecornerpoint0 = cornerpoint0 + clonedplanegeometry->GetAxisVector(1); //+clonedplanegeometry->GetAxisVector(2); CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of sagitally initialized version: ", mitk::Equal(planegeometry->GetCornerPoint(0), backsidecornerpoint0, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of backsidedly, axially initialized version " "(should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of backsidedly, axially initialized version " "(should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in units) of backsidedly, axially initialized version " "(should be same as in mm due to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width, height and thickness (in mm) of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), -bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of backsidedly, axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); // T22254: Flipped sign mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, backsideorigin, right, -bottom); } void TestFrontalInitialization() { mitk::Point3D cornerpoint0 = planegeometry->GetCornerPoint(0); mitk::PlaneGeometry::Pointer clonedplanegeometry = dynamic_cast(planegeometry->Clone().GetPointer()); //-------- mitk::Vector3D newright, newbottom, newnormal; mitk::ScalarType newthicknessInMM; // Testing InitializeStandardPlane(clonedplanegeometry, planeorientation = Frontal, zPosition = 0, frontside=true) planegeometry->InitializeStandardPlane(clonedplanegeometry, mitk::PlaneGeometry::Frontal); newright = right; newbottom = normal; newbottom.Normalize(); newbottom *= thicknessInMM; newthicknessInMM = heightInMM / height * 1.0 /*extent in normal direction is 1*/; newnormal = -bottom; newnormal.Normalize(); newnormal *= newthicknessInMM; CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of frontally initialized version: ", mitk::Equal(planegeometry->GetCornerPoint(0), cornerpoint0, testEps)); // ok, corner was fine, so we can dare to believe the origin is ok. origin = planegeometry->GetOrigin(); CPPUNIT_ASSERT_MESSAGE("Testing width (in units) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in units) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in units) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), newthicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), -newnormal, testEps)); // T22254: Flipped sign mappingTests2D(planegeometry, width, 1, widthInMM, thicknessInMM, origin, newright, newbottom); // Changing plane to in-plane unit spacing using SetSizeInUnits: planegeometry->SetSizeInUnits(planegeometry->GetExtentInMM(0), planegeometry->GetExtentInMM(1)); CPPUNIT_ASSERT_MESSAGE("Testing origin of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), newthicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), -newnormal, testEps)); // T22254: Flipped sign mappingTests2D(planegeometry, widthInMM, thicknessInMM, widthInMM, thicknessInMM, origin, newright, newbottom); // Changing plane to unit spacing also in normal direction using SetExtentInMM(2, 1.0): planegeometry->SetExtentInMM(2, 1.0); newnormal.Normalize(); CPPUNIT_ASSERT_MESSAGE("Testing origin of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetOrigin(), origin, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in units) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE( "Testing width, height and thickness (in mm) of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), 1.0, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), newright, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), newbottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of unit spaced, frontally initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), -newnormal, testEps)); // T22254: Flipped sign mappingTests2D(planegeometry, widthInMM, thicknessInMM, widthInMM, thicknessInMM, origin, newright, newbottom); } void TestAxialInitialization() { mitk::Point3D cornerpoint0 = planegeometry->GetCornerPoint(0); // Clone, move, rotate and test for 'IsParallel' and 'IsOnPlane' mitk::PlaneGeometry::Pointer clonedplanegeometry = dynamic_cast(planegeometry->Clone().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Testing Clone(): ", !((clonedplanegeometry.IsNull()) || (clonedplanegeometry->GetReferenceCount() != 1))); std::cout << "Testing InitializeStandardPlane(clonedplanegeometry, planeorientation = Axial, zPosition = 0, " "frontside=true): " << std::endl; planegeometry->InitializeStandardPlane(clonedplanegeometry); CPPUNIT_ASSERT_MESSAGE("Testing origin of axially initialized version: ", mitk::Equal(planegeometry->GetOrigin(), origin)); CPPUNIT_ASSERT_MESSAGE("Testing GetCornerPoint(0) of axially initialized version: ", mitk::Equal(planegeometry->GetCornerPoint(0), cornerpoint0)); CPPUNIT_ASSERT_MESSAGE("Testing width (in units) of axially initialized version (should be same as in mm due to " "unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(0), width, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in units) of axially initialized version (should be same as in mm due to " "unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(1), height, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in units) of axially initialized version (should be same as in mm due " "to unit spacing, except for thickness, which is always 1): ", mitk::Equal(planegeometry->GetExtent(2), 1, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing width (in mm) of axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(0), widthInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing height (in mm) of axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(1), heightInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing thickness (in mm) of axially initialized version: ", mitk::Equal(planegeometry->GetExtentInMM(2), thicknessInMM, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(0), right, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(1), bottom, testEps)); CPPUNIT_ASSERT_MESSAGE("Testing GetAxisVector() of axially initialized version: ", mitk::Equal(planegeometry->GetAxisVector(2), normal, testEps)); mappingTests2D(planegeometry, width, height, widthInMM, heightInMM, origin, right, bottom); } void TestPlaneComparison() { // Clone, move, rotate and test for 'IsParallel' and 'IsOnPlane' mitk::PlaneGeometry::Pointer clonedplanegeometry2 = dynamic_cast(planegeometry->Clone().GetPointer()); CPPUNIT_ASSERT_MESSAGE("Testing Clone(): ", !((clonedplanegeometry2.IsNull()) || (clonedplanegeometry2->GetReferenceCount() != 1))); CPPUNIT_ASSERT_MESSAGE("Testing wheter original and clone are at the same position", clonedplanegeometry2->IsOnPlane(planegeometry.GetPointer())); CPPUNIT_ASSERT_MESSAGE(" Asserting that origin is on the plane cloned plane:", clonedplanegeometry2->IsOnPlane(origin)); mitk::VnlVector newaxis(3); mitk::FillVector3D(newaxis, 1.0, 1.0, 1.0); newaxis.normalize(); vnl_quaternion rotation2(newaxis, 0.0); mitk::Vector3D clonednormal = clonedplanegeometry2->GetNormal(); mitk::Point3D clonedorigin = clonedplanegeometry2->GetOrigin(); auto planerot = new mitk::RotationOperation(mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector(0), 180.0); clonedplanegeometry2->ExecuteOperation(planerot); CPPUNIT_ASSERT_MESSAGE(" Asserting that a flipped plane is still on the original plane: ", clonedplanegeometry2->IsOnPlane(planegeometry.GetPointer())); clonedorigin += clonednormal; clonedplanegeometry2->SetOrigin(clonedorigin); CPPUNIT_ASSERT_MESSAGE("Testing if the translated (cloned, flipped) plane is parallel to its origin plane: ", clonedplanegeometry2->IsParallel(planegeometry)); delete planerot; planerot = new mitk::RotationOperation(mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector(0), 0.5); clonedplanegeometry2->ExecuteOperation(planerot); CPPUNIT_ASSERT_MESSAGE("Testing if a non-paralell plane gets recognized as not paralell [rotation +0.5 degree] : ", !clonedplanegeometry2->IsParallel(planegeometry)); delete planerot; planerot = new mitk::RotationOperation(mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector(0), -1.0); clonedplanegeometry2->ExecuteOperation(planerot); CPPUNIT_ASSERT_MESSAGE("Testing if a non-paralell plane gets recognized as not paralell [rotation -0.5 degree] : ", !clonedplanegeometry2->IsParallel(planegeometry)); delete planerot; planerot = new mitk::RotationOperation(mitk::OpROTATE, origin, clonedplanegeometry2->GetAxisVector(0), 360.5); clonedplanegeometry2->ExecuteOperation(planerot); CPPUNIT_ASSERT_MESSAGE("Testing if a non-paralell plane gets recognized as paralell [rotation 360 degree] : ", clonedplanegeometry2->IsParallel(planegeometry)); } private: // helper Methods for the Tests mitk::PlaneGeometry::Pointer createPlaneGeometry() { mitk::Vector3D mySpacing; mySpacing[0] = 31; mySpacing[1] = 0.1; mySpacing[2] = 5.4; mitk::Point3D myOrigin; myOrigin[0] = 8; myOrigin[1] = 9; myOrigin[2] = 10; mitk::AffineTransform3D::Pointer myTransform = mitk::AffineTransform3D::New(); itk::Matrix transMatrix; transMatrix.Fill(0); transMatrix[0][0] = 1; transMatrix[1][1] = 2; transMatrix[2][2] = 4; myTransform->SetMatrix(transMatrix); mitk::PlaneGeometry::Pointer geometry2D = mitk::PlaneGeometry::New(); geometry2D->SetIndexToWorldTransform(myTransform); geometry2D->SetSpacing(mySpacing); geometry2D->SetOrigin(myOrigin); return geometry2D; } bool compareMatrix(itk::Matrix left, itk::Matrix right) { bool equal = true; for (int i = 0; i < 3; ++i) for (int j = 0; j < 3; ++j) equal &= mitk::Equal(left[i][j], right[i][j]); return equal; } /** * This function tests for correct mapping and is called several times from other tests **/ void mappingTests2D(const mitk::PlaneGeometry *planegeometry, const mitk::ScalarType &width, const mitk::ScalarType &height, const mitk::ScalarType &widthInMM, const mitk::ScalarType &heightInMM, const mitk::Point3D &origin, const mitk::Vector3D &right, const mitk::Vector3D &bottom) { std::cout << "Testing mapping Map(pt2d_mm(x=widthInMM/2.3,y=heightInMM/2.5), pt3d_mm) and compare with expected: "; mitk::Point2D pt2d_mm; mitk::Point3D pt3d_mm, expected_pt3d_mm; pt2d_mm[0] = widthInMM / 2.3; pt2d_mm[1] = heightInMM / 2.5; expected_pt3d_mm = origin + right * (pt2d_mm[0] / right.GetNorm()) + bottom * (pt2d_mm[1] / bottom.GetNorm()); planegeometry->Map(pt2d_mm, pt3d_mm); CPPUNIT_ASSERT_MESSAGE( "Testing mapping Map(pt2d_mm(x=widthInMM/2.3,y=heightInMM/2.5), pt3d_mm) and compare with expected", mitk::Equal(pt3d_mm, expected_pt3d_mm, testEps)); std::cout << "Testing mapping Map(pt3d_mm, pt2d_mm) and compare with expected: "; mitk::Point2D testpt2d_mm; planegeometry->Map(pt3d_mm, testpt2d_mm); std::cout << std::setprecision(12) << "Expected pt2d_mm " << pt2d_mm << std::endl; std::cout << std::setprecision(12) << "Result testpt2d_mm " << testpt2d_mm << std::endl; std::cout << std::setprecision(12) << "10*mitk::eps " << 10 * mitk::eps << std::endl; // This eps is temporarily set to 10*mitk::eps. See bug #15037 for details. CPPUNIT_ASSERT_MESSAGE("Testing mapping Map(pt3d_mm, pt2d_mm) and compare with expected", mitk::Equal(pt2d_mm, testpt2d_mm, 10 * mitk::eps)); std::cout << "Testing IndexToWorld(pt2d_units, pt2d_mm) and compare with expected: "; mitk::Point2D pt2d_units; pt2d_units[0] = width / 2.0; pt2d_units[1] = height / 2.0; pt2d_mm[0] = widthInMM / 2.0; pt2d_mm[1] = heightInMM / 2.0; planegeometry->IndexToWorld(pt2d_units, testpt2d_mm); std::cout << std::setprecision(12) << "Expected pt2d_mm " << pt2d_mm << std::endl; std::cout << std::setprecision(12) << "Result testpt2d_mm " << testpt2d_mm << std::endl; std::cout << std::setprecision(12) << "10*mitk::eps " << 10 * mitk::eps << std::endl; // This eps is temporarily set to 10*mitk::eps. See bug #15037 for details. CPPUNIT_ASSERT_MESSAGE("Testing IndexToWorld(pt2d_units, pt2d_mm) and compare with expected: ", mitk::Equal(pt2d_mm, testpt2d_mm, 10 * mitk::eps)); std::cout << "Testing WorldToIndex(pt2d_mm, pt2d_units) and compare with expected: "; mitk::Point2D testpt2d_units; planegeometry->WorldToIndex(pt2d_mm, testpt2d_units); std::cout << std::setprecision(12) << "Expected pt2d_units " << pt2d_units << std::endl; std::cout << std::setprecision(12) << "Result testpt2d_units " << testpt2d_units << std::endl; std::cout << std::setprecision(12) << "10*mitk::eps " << 10 * mitk::eps << std::endl; // This eps is temporarily set to 10*mitk::eps. See bug #15037 for details. CPPUNIT_ASSERT_MESSAGE("Testing WorldToIndex(pt2d_mm, pt2d_units) and compare with expected:", mitk::Equal(pt2d_units, testpt2d_units, 10 * mitk::eps)); } }; MITK_TEST_SUITE_REGISTRATION(mitkPlaneGeometry) diff --git a/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp index 7a246e42dc..6ef8df3be1 100644 --- a/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp +++ b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp @@ -1,919 +1,919 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPropertyRelationRuleBase.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include /** This class is used to test PropertyRelationRuleBase and get access to internals where needed to test them as well. */ namespace mitk { class TestRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(TestRule, mitk::PropertyRelationRuleBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; RuleIDType GetRuleID() const override { if (m_AbstractMode) { return "TestRule"; } else { return "TestRule_type1"; } }; std::string GetDisplayName() const override { return "TestDisplayName"; } std::string GetSourceRoleName() const override { return "source role"; } std::string GetDestinationRoleName() const override { return "destination role"; } bool m_AbstractMode; bool IsAbstract() const override { return m_AbstractMode; } using Superclass::GetRootKeyPath; using Superclass::Connect; protected: TestRule() : m_AbstractMode(false) { }; ~TestRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; bool IsSupportedRuleID(const RuleIDType& ruleID) const override { if (m_AbstractMode) { return ruleID.find(this->GetRuleID()) == 0; } else { return Superclass::IsSupportedRuleID(ruleID); } }; InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, const IPropertyProvider *destination) const override { InstanceIDVectorType result; auto destProp = destination->GetConstProperty("name"); if (destProp.IsNotNull()) { auto destRegExStr = PropertyKeyPathToPropertyRegEx(Superclass::GetRootKeyPath().AddAnyElement().AddElement("dataHandle")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; auto keys = source->GetPropertyKeys(); for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto sourceProp = source->GetConstProperty(key); if (sourceProp->GetValueAsString() == destProp->GetValueAsString()) { if (instance_matches.size()>1) { result.push_back(instance_matches[1]); } } } } } return result; }; bool HasImplicitDataRelation(const IPropertyProvider *source, const IPropertyProvider *destination) const override { auto destProp = destination->GetConstProperty("name"); auto sourceProp = source->GetConstProperty("referencedName"); return destProp.IsNotNull() && sourceProp.IsNotNull() && destProp->GetValueAsString() == sourceProp->GetValueAsString(); }; void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override { auto destProp = destination->GetConstProperty("name"); source->SetProperty("referencedName", StringProperty::New(destProp->GetValueAsString())); source->SetProperty( PropertyKeyPathToPropertyName(Superclass::GetRootKeyPath().AddElement(instanceID).AddElement("dataHandle")), StringProperty::New(destProp->GetValueAsString())); }; void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType & /*instanceID*/) const override { source->RemoveProperty("referencedName"); }; }; } // namespace mitk class mitkPropertyRelationRuleBaseTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPropertyRelationRuleBaseTestSuite); MITK_TEST(GetRootKeyPath); MITK_TEST(IsSourceCandidate); MITK_TEST(IsDestinationCandidate); MITK_TEST(IsSource); MITK_TEST(HasRelation); MITK_TEST(GetExistingRelations); MITK_TEST(GetRelationUIDs); MITK_TEST(GetSourceCandidateIndicator); MITK_TEST(GetDestinationCandidateIndicator); MITK_TEST(GetConnectedSourcesDetector); MITK_TEST(GetSourcesDetector); MITK_TEST(GetDestinationsDetector); MITK_TEST(GetDestinationDetector); MITK_TEST(Connect); MITK_TEST(Disconnect); MITK_TEST(Connect_abstract); MITK_TEST(Disconnect_abstract); CPPUNIT_TEST_SUITE_END(); private: mitk::TestRule::Pointer rule; mitk::TestRule::Pointer abstractRule; mitk::DataNode::Pointer unRelated; mitk::PointSet::Pointer unRelated_1_data; mitk::DataNode::Pointer source_implicit_1; mitk::DataNode::Pointer source_data_1; mitk::DataNode::Pointer source_idOnly_1; mitk::DataNode::Pointer source_1; mitk::DataNode::Pointer source_multi; mitk::DataNode::Pointer source_otherRule; mitk::DataNode::Pointer source_otherTypeRule; //relevant for abstract rule checks. Abstract rule should see it concrete rule not. mitk::DataNode::Pointer dest_1; mitk::PointSet::Pointer dest_1_data; mitk::DataNode::Pointer dest_2; mitk::PointSet::Pointer dest_2_data; bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const { auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath(); if (!instance.empty()) { keyPath.AddElement(instance); } auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath); auto keys = provider->GetPropertyKeys(); for (const auto &key : keys) { if (key.find(prefix) == 0) { return true; } } return false; } public: void setUp() override { rule = mitk::TestRule::New(); abstractRule = mitk::TestRule::New(); abstractRule->m_AbstractMode = true; unRelated = mitk::DataNode::New(); unRelated->SetName("unRelated"); unRelated_1_data = mitk::PointSet::New(); unRelated->SetData(unRelated_1_data); dest_1 = mitk::DataNode::New(); dest_1->SetName("dest_1"); dest_1_data = mitk::PointSet::New(); dest_1->SetData(dest_1_data); dest_2 = mitk::DataNode::New(); dest_2->SetName("dest_2"); dest_2_data = mitk::PointSet::New(); dest_2->SetData(dest_2_data); source_implicit_1 = mitk::DataNode::New(); source_implicit_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); source_idOnly_1 = mitk::DataNode::New(); std::string name = "MITK.Relations.1.relationUID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid1")); name = "MITK.Relations.1.destinationUID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_data_1 = source_implicit_1->Clone(); name = "MITK.Relations.1.relationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid2")); name = "MITK.Relations.1.dataHandle"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.ruleID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid10"), nullptr, true); name = "MITK.Relations.2.destinationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.2.ruleID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); source_1 = source_data_1->Clone(); name = "MITK.Relations.1.relationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid3"), nullptr, true); name = "MITK.Relations.1.destinationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid8"), nullptr, true); name = "MITK.Relations.2.destinationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_2_data->GetUID())); name = "MITK.Relations.2.ruleID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); source_multi = source_1->Clone(); name = "MITK.Relations.1.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid4"), nullptr, true); name = "MITK.Relations.1.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.4.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid5")); name = "MITK.Relations.4.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_2_data->GetUID())); name = "MITK.Relations.4.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid6")); name = "MITK.Relations.2.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("unknown")); name = "MITK.Relations.2.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_otherRule = mitk::DataNode::New(); name = "MITK.Relations.1.relationUID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New("uid7")); name = "MITK.Relations.1.destinationUID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New("otherRuleID")); source_otherTypeRule = mitk::DataNode::New(); name = "MITK.Relations.1.relationUID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New("uid9")); name = "MITK.Relations.1.destinationUID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); } void tearDown() override {} void GetRootKeyPath() { mitk::PropertyKeyPath ref; ref.AddElement("MITK").AddElement("Relations"); CPPUNIT_ASSERT(mitk::PropertyRelationRuleBase::GetRootKeyPath() == ref); } void IsSourceCandidate() { CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr)); } void IsDestinationCandidate() { CPPUNIT_ASSERT(rule->IsDestinationCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr)); } void IsSource() { CPPUNIT_ASSERT_THROW_MESSAGE( "Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->IsSource(unRelated)); CPPUNIT_ASSERT(!rule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(rule->IsSource(source_data_1)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(rule->IsSource(source_1)); CPPUNIT_ASSERT(rule->IsSource(source_multi)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule)); CPPUNIT_ASSERT(!rule->IsSource(source_otherTypeRule)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_data_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_multi)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherTypeRule)); } void HasRelation() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->HasRelation(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->HasRelation(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(rule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(rule->HasRelation(source_otherTypeRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_Data); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(abstractRule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherTypeRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); } void GetExistingRelations() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetExistingRelations(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherTypeRule).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_implicit_1).empty()); auto uids = rule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = rule->GetExistingRelations(source_data_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid2"); uids = rule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); uids = rule->GetExistingRelations(source_multi); CPPUNIT_ASSERT(uids.size() == 3); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid4") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid5") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid6") != uids.end()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_otherRule).empty()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_implicit_1).empty()); uids = abstractRule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = abstractRule->GetExistingRelations(source_data_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); uids = abstractRule->GetExistingRelations(source_multi); CPPUNIT_ASSERT(uids.size() == 3); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid4") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid5") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid6") != uids.end()); uids = abstractRule->GetExistingRelations(source_otherTypeRule); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid9"); } void GetRelationUIDs() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetRelationUIDs(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetRelationUIDs(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherTypeRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_data_1, dest_1).front() == "uid2"); auto uids = rule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_multi, dest_1).front() == "uid4"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_multi, dest_2).front() == "uid5"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherTypeRule, dest_1).front() == "uid9"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); uids = abstractRule->GetRelationUIDs(source_data_1, dest_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_2); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, dest_1).front() == "uid3"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_multi, dest_1).front() == "uid4"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_multi, dest_2).front() == "uid5"); } void GetSourceCandidateIndicator() { auto predicate = rule->GetSourceCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetDestinationCandidateIndicator() { auto predicate = rule->GetDestinationCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetConnectedSourcesDetector() { auto predicate = rule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); auto predicate2 = abstractRule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate2->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate2->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate2->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_multi)); CPPUNIT_ASSERT(!predicate2->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate2->CheckNode(source_otherTypeRule)); } void GetSourcesDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetSourcesDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); predicate = abstractRule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); } void GetDestinationsDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationsDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetDestinationsDetector(source_otherRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); predicate = abstractRule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = abstractRule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); } void GetDestinationDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationDetector(nullptr, "uid1"), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (relation uid is invalid) does not throw.", rule->GetDestinationDetector(source_1, "invalid uid"), itk::ExceptionObject); auto predicate = rule->GetDestinationDetector(source_1, "uid3"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationDetector(source_multi, "uid5"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); } void Connect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Connect(source_1, nullptr), itk::ExceptionObject); // check upgrade of an implicit connection CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); rule->Connect(source_implicit_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); // check upgrade of an data connection CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_Data); rule->Connect(source_data_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); std::string name = "MITK.Relations.1.destinationUID"; auto prop = source_data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); - CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was not stored.", prop->GetValueAsString() == dest_1_data->GetUID()); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1_data->GetUID()); // check actualization of an id only connection rule->Connect(source_idOnly_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); name = "MITK.Relations.1.dataHandle"; prop = source_idOnly_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); - CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was not stored.", + CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information not stored.", prop->GetValueAsString() == dest_1->GetName()); prop = source_idOnly_1->GetProperty("referencedName"); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); - CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was not stored.", + CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == dest_1->GetName()); // check actualization of an existing connection rule->Connect(source_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); // check new connection auto newConnectUID = rule->Connect(source_multi, unRelated); CPPUNIT_ASSERT(rule->HasRelation(source_multi, unRelated) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); name = "MITK.Relations.5.dataHandle"; prop = source_multi->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); - CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was not stored.", + CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == unRelated->GetName()); prop = source_multi->GetProperty("referencedName"); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); - CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was not stored.", + CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == unRelated->GetName()); name = "MITK.Relations.5.destinationUID"; prop = source_multi->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); - CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was not stored.", prop->GetValueAsString() == unRelated_1_data->GetUID()); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated_1_data->GetUID()); auto storedRelationUIDs = rule->GetRelationUIDs(source_multi, unRelated); CPPUNIT_ASSERT_MESSAGE( "Relation uid was not stored for given source and destination.", storedRelationUIDs.size() == 1); - CPPUNIT_ASSERT_MESSAGE("Incorrect Relation uid was not stored.", storedRelationUIDs[0] == newConnectUID); + CPPUNIT_ASSERT_MESSAGE("Incorrect Relation uid was stored.", storedRelationUIDs[0] == newConnectUID); } void Disconnect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid"), itk::ExceptionObject); CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); rule->Disconnect(source_1, unRelated); CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT_MESSAGE("Data property was not removed.", !source_1->GetProperty("referencedName")); rule->Disconnect(source_1, dest_2); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(this->hasRelationProperties(source_1)); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); rule->Disconnect(source_1, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.",this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); rule->Disconnect(source_multi, dest_1); CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_multi, "uid6"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_multi, "unkownRelationUID"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_otherTypeRule, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", this->hasRelationProperties(source_otherTypeRule, "1")); } void Connect_abstract() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(source_1, nullptr), itk::ExceptionObject); } void Disconnect_abstract() { CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); abstractRule->Disconnect(source_1, dest_2); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); abstractRule->Disconnect(source_1, dest_1); CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); abstractRule->Disconnect(source_multi, dest_1); CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_multi, "uid6"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_multi, "unkownRelationUID"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_otherTypeRule, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", !this->hasRelationProperties(source_otherTypeRule, "1")); } }; MITK_TEST_SUITE_REGISTRATION(mitkPropertyRelationRuleBase) diff --git a/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp b/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp new file mode 100644 index 0000000000..bb97c0baf0 --- /dev/null +++ b/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp @@ -0,0 +1,884 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkSourceImageRelationRule.h" + +#include "mitkDataNode.h" +#include "mitkPointSet.h" +#include "mitkStringProperty.h" + +#include "mitkTestFixture.h" +#include "mitkTestingMacros.h" +#include "mitkPropertyNameHelper.h" +#include "mitkTemporoSpatialStringProperty.h" +#include "mitkPropertyNameHelper.h" + +#include + +class mitkSourceImageRelationRuleTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkSourceImageRelationRuleTestSuite); + + MITK_TEST(IsSourceCandidate); + MITK_TEST(IsDestinationCandidate); + MITK_TEST(IsSource); + MITK_TEST(HasRelation); + MITK_TEST(GetExistingRelations); + MITK_TEST(GetRelationUIDs); + MITK_TEST(GetSourceCandidateIndicator); + MITK_TEST(GetDestinationCandidateIndicator); + MITK_TEST(GetConnectedSourcesDetector); + MITK_TEST(GetSourcesDetector); + MITK_TEST(GetDestinationsDetector); + MITK_TEST(GetDestinationDetector); + MITK_TEST(Connect); + MITK_TEST(Disconnect); + MITK_TEST(Connect_abstract); + MITK_TEST(Disconnect_abstract); + + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::SourceImageRelationRule::Pointer rule; + mitk::SourceImageRelationRule::Pointer abstractRule; + + mitk::Image::Pointer unRelated; + mitk::DataNode::Pointer unRelated_Node; + + mitk::Image::Pointer source_implicit_1; + mitk::DataNode::Pointer source_implicit_1_Node; + mitk::Image::Pointer source_data_1; + mitk::DataNode::Pointer source_data_1_Node; + mitk::Image::Pointer source_idOnly_1; + mitk::DataNode::Pointer source_idOnly_1_Node; + mitk::Image::Pointer source_1; + mitk::DataNode::Pointer source_1_Node; + + mitk::Image::Pointer source_otherRule; + mitk::DataNode::Pointer source_otherRule_Node; + + mitk::Image::Pointer source_otherPurpose; + mitk::DataNode::Pointer source_otherPurpose_Node; //relevant for abstract rule checks. Abstract rule should see it concrete rule not. + + mitk::DataNode::Pointer dest_1_Node; + mitk::Image::Pointer dest_1; + mitk::DataNode::Pointer dest_2_Node; + mitk::Image::Pointer dest_2; + + bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const + { + auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath(); + if (!instance.empty()) + { + keyPath.AddElement(instance); + } + + auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath); + auto keys = provider->GetPropertyKeys(); + + for (const auto &key : keys) + { + if (key.find(prefix) == 0) + { + return true; + } + } + + return false; + } + + std::vector GetReferenceSequenceIndices(const mitk::IPropertyProvider * source, + const mitk::IPropertyProvider * destination) const + { + std::vector result; + + auto destInstanceUIDProp = destination->GetConstProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018)); + + if (destInstanceUIDProp.IsNull()) + { + return result; + } + + mitk::PropertyKeyPath referencedInstanceUIDs; + referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); + + auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);; + auto regEx = std::regex(sourceRegExStr); + + std::vector keys; + //workaround until T24729 is done. Please remove if T24728 is done + keys = source->GetPropertyKeys(); + //end workaround for T24729 + + for (const auto &key : keys) + { + if (std::regex_match(key, regEx)) + { + auto refUIDProp = source->GetConstProperty(key); + if (*refUIDProp == *destInstanceUIDProp) + { + mitk::PropertyKeyPath finding = mitk::PropertyNameToPropertyKeyPath(key); + result.push_back(std::to_string(finding.GetNode(2).selection)); + } + } + } + + return result; + }; + + void SetDICOMReferenceInfo(mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) + { + mitk::PropertyKeyPath refInstanceUIDPath; + refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); + owner->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), mitk::TemporoSpatialStringProperty::New(instanceUID)); + + mitk::PropertyKeyPath refClassUIDPath; + refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); + owner->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), mitk::TemporoSpatialStringProperty::New(classUID)); + + mitk::PropertyKeyPath purposePath; + purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); + owner->SetProperty(PropertyKeyPathToPropertyName(purposePath), mitk::TemporoSpatialStringProperty::New(purpose)); + } + + bool IsCorrectDICOMReference(const mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) const + { + mitk::PropertyKeyPath refInstanceUIDPath; + refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); + auto prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath)); + if (prop->GetValueAsString() != instanceUID) + { + return false; + } + + mitk::PropertyKeyPath refClassUIDPath; + refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); + prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refClassUIDPath)); + if (prop->GetValueAsString() != classUID) + { + return false; + } + + mitk::PropertyKeyPath purposePath; + purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); + prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); + if (prop->GetValueAsString() != purpose) + { + return false; + } + + return true; + } + + +public: + void setUp() override + { + auto instanceUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018); + auto classUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0016); + + rule = mitk::SourceImageRelationRule::New("Test"); + + abstractRule = mitk::SourceImageRelationRule::New(); + + unRelated = mitk::Image::New(); + unRelated->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("unRelated")); + unRelated->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); + unRelated_Node = mitk::DataNode::New(); + unRelated_Node->SetData(unRelated); + + dest_1_Node = mitk::DataNode::New(); + dest_1_Node->SetName("dest_1"); + dest_1 = mitk::Image::New(); + dest_1->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_1")); + dest_1->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); + dest_1_Node->SetData(dest_1); + + + + dest_2_Node = mitk::DataNode::New(); + dest_2_Node->SetName("dest_2"); + dest_2 = mitk::Image::New(); + dest_2->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_2")); + dest_2->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); + dest_2_Node->SetData(dest_2); + + source_implicit_1 = mitk::Image::New(); + SetDICOMReferenceInfo(source_implicit_1, "dest_1", "image", "Test", 0); + source_implicit_1_Node = mitk::DataNode::New(); + source_implicit_1_Node->SetData(source_implicit_1); + + source_idOnly_1 = mitk::Image::New(); + std::string name = "MITK.Relations.1.relationUID"; + source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid1")); + name = "MITK.Relations.1.destinationUID"; + source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); + name = "MITK.Relations.1.ruleID"; + source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); + source_idOnly_1_Node = mitk::DataNode::New(); + source_idOnly_1_Node->SetData(source_idOnly_1); + + source_data_1 = mitk::Image::New(); + SetDICOMReferenceInfo(source_data_1, "dest_1", "image", "Test", 0); + SetDICOMReferenceInfo(source_data_1, "dest_2", "image", "otherpurpose", 1); + name = "MITK.Relations.1.relationUID"; + source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid2")); + name = "MITK.Relations.1.ruleID"; + source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); + name = "MITK.Relations.1.SourceImageSequenceItem"; + source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); + name = "MITK.Relations.2.relationUID"; + source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid10")); + name = "MITK.Relations.2.SourceImageSequenceItem"; + source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); + name = "MITK.Relations.2.ruleID"; + source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); + source_data_1_Node = mitk::DataNode::New(); + source_data_1_Node->SetData(source_data_1); + + source_1 = mitk::Image::New(); + SetDICOMReferenceInfo(source_1, "dest_1", "image", "Test", 0); + SetDICOMReferenceInfo(source_1, "dest_2", "image", "otherpurpose", 1); + name = "MITK.Relations.1.relationUID"; + source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid3")); + name = "MITK.Relations.1.destinationUID"; + source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); + name = "MITK.Relations.1.ruleID"; + source_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); + name = "MITK.Relations.1.SourceImageSequenceItem"; + source_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); + name = "MITK.Relations.2.relationUID"; + source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid8")); + name = "MITK.Relations.2.destinationUID"; + source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_2->GetUID())); + name = "MITK.Relations.2.ruleID"; + source_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); + name = "MITK.Relations.2.SourceImageSequenceItem"; + source_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); + source_1_Node = mitk::DataNode::New(); + source_1_Node->SetData(source_1); + + source_otherRule = mitk::Image::New(); + name = "MITK.Relations.1.relationUID"; + source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("uid7")); + name = "MITK.Relations.1.destinationUID"; + source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); + name = "MITK.Relations.1.ruleID"; + source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("otherRuleID")); + source_otherRule_Node = mitk::DataNode::New(); + source_otherRule_Node->SetData(source_otherRule); + + source_otherPurpose = mitk::Image::New(); + name = "MITK.Relations.1.relationUID"; + source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("uid9")); + name = "MITK.Relations.1.destinationUID"; + source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); + name = "MITK.Relations.1.ruleID"; + source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); + source_otherPurpose_Node = mitk::DataNode::New(); + source_otherPurpose_Node->SetData(source_otherPurpose); + } + + void tearDown() override {} + + void IsSourceCandidate() + { + CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New())); + CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr)); + } + + void IsDestinationCandidate() + { + CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1_Node)); + CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1)); + CPPUNIT_ASSERT(!rule->IsDestinationCandidate(mitk::DataNode::New())); + CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr)); + } + + void IsSource() + { + CPPUNIT_ASSERT_THROW_MESSAGE( + "Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject); + + CPPUNIT_ASSERT(!rule->IsSource(unRelated)); + CPPUNIT_ASSERT(!rule->IsSource(source_implicit_1)); + CPPUNIT_ASSERT(rule->IsSource(source_data_1)); + CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1)); + CPPUNIT_ASSERT(rule->IsSource(source_1)); + + CPPUNIT_ASSERT(!rule->IsSource(source_otherRule)); + CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose)); + + CPPUNIT_ASSERT(!rule->IsSource(source_implicit_1_Node)); + CPPUNIT_ASSERT(rule->IsSource(source_data_1_Node)); + CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1_Node)); + CPPUNIT_ASSERT(rule->IsSource(source_1_Node)); + + CPPUNIT_ASSERT(!rule->IsSource(source_otherRule_Node)); + CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose_Node)); + + + CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated)); + CPPUNIT_ASSERT(!abstractRule->IsSource(source_implicit_1)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_data_1)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_1)); + + CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose)); + + CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated_Node)); + CPPUNIT_ASSERT(!abstractRule->IsSource(source_implicit_1_Node)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_data_1_Node)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1_Node)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_1_Node)); + + CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule_Node)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose_Node)); + } + + void HasRelation() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->HasRelation(nullptr, dest_1), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->HasRelation(source_1, nullptr), + itk::ExceptionObject); + + CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(rule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(rule->HasRelation(source_otherPurpose, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); + + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(abstractRule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + } + + void GetExistingRelations() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->GetExistingRelations(nullptr), + itk::ExceptionObject); + + CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty()); + CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty()); + CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherPurpose).empty()); + CPPUNIT_ASSERT(rule->GetExistingRelations(source_implicit_1).empty()); + + auto uids = rule->GetExistingRelations(source_idOnly_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == "uid1"); + + uids = rule->GetExistingRelations(source_data_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == "uid2"); + + uids = rule->GetExistingRelations(source_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == "uid3"); + + + CPPUNIT_ASSERT(abstractRule->GetExistingRelations(unRelated).empty()); + CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_otherRule).empty()); + CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_implicit_1).empty()); + + uids = abstractRule->GetExistingRelations(source_idOnly_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == "uid1"); + + uids = abstractRule->GetExistingRelations(source_data_1); + CPPUNIT_ASSERT(uids.size() == 2); + CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); + CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); + + uids = abstractRule->GetExistingRelations(source_1); + CPPUNIT_ASSERT(uids.size() == 2); + CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); + CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); + + uids = abstractRule->GetExistingRelations(source_otherPurpose); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == "uid9"); + } + + void GetRelationUIDs() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->GetRelationUIDs(nullptr, dest_1), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->GetRelationUIDs(source_1, nullptr), + itk::ExceptionObject); + + CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty()); + CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty()); + CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty()); + CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty()); + CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherPurpose, dest_1).empty()); + + CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); + CPPUNIT_ASSERT(rule->GetRelationUIDs(source_data_1, dest_1).front() == "uid2"); + auto uids = rule->GetRelationUIDs(source_1, dest_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == "uid3"); + + CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, unRelated).empty()); + CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(unRelated, dest_1).empty()); + CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherRule, dest_1).empty()); + CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherPurpose, dest_1).front() == "uid9"); + + CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); + uids = abstractRule->GetRelationUIDs(source_data_1, dest_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); + uids = abstractRule->GetRelationUIDs(source_1, dest_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); + uids = abstractRule->GetRelationUIDs(source_1, dest_2); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); + } + + void GetSourceCandidateIndicator() + { + auto predicate = rule->GetSourceCandidateIndicator(); + + CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); + CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); + } + + void GetDestinationCandidateIndicator() + { + auto predicate = rule->GetDestinationCandidateIndicator(); + + CPPUNIT_ASSERT(predicate->CheckNode(this->dest_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(mitk::DataNode::New())); + CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); + } + + void GetConnectedSourcesDetector() + { + auto predicate = rule->GetConnectedSourcesDetector(); + + CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); + + + auto predicate2 = abstractRule->GetConnectedSourcesDetector(); + + CPPUNIT_ASSERT(!predicate2->CheckNode(nullptr)); + CPPUNIT_ASSERT(!predicate2->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate2->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(predicate2->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate2->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(predicate2->CheckNode(source_1_Node)); + + CPPUNIT_ASSERT(!predicate2->CheckNode(source_otherRule_Node)); + CPPUNIT_ASSERT(predicate2->CheckNode(source_otherPurpose_Node)); + } + + void GetSourcesDetector() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->GetSourcesDetector(nullptr), + itk::ExceptionObject); + + auto predicate = rule->GetSourcesDetector(dest_1); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); + + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); + + CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); + + CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + + predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); + + CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_1_Node)); + + predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); + + CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + + predicate = abstractRule->GetSourcesDetector(dest_1); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); + + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + } + + void GetDestinationsDetector() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->GetDestinationsDetector(nullptr), + itk::ExceptionObject); + + auto predicate = rule->GetDestinationsDetector(source_otherRule); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); + + predicate = rule->GetDestinationsDetector(source_otherPurpose); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); + + predicate = rule->GetDestinationsDetector(source_implicit_1); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); + + predicate = rule->GetDestinationsDetector(source_data_1); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); + + predicate = rule->GetDestinationsDetector(source_idOnly_1); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + + predicate = rule->GetDestinationsDetector(source_1); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); + predicate = + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); + predicate = + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); + + predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + } + + void GetDestinationDetector() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->GetDestinationDetector(nullptr, "uid1"), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (relation uid is invalid) does not throw.", + rule->GetDestinationDetector(source_1, "invalid uid"), + itk::ExceptionObject); + + auto predicate = rule->GetDestinationDetector(source_1, "uid3"); + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); + } + + void Connect() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->Connect(nullptr, dest_1), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->Connect(source_1, nullptr), + itk::ExceptionObject); + + // check upgrade of an implicit connection + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + rule->Connect(source_implicit_1, dest_1); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + auto dcmRefs = GetReferenceSequenceIndices(source_implicit_1, dest_1); + CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_implicit_1, "dest_1", "image", "Test", 0)); + + // check upgrade and reuse of an data connection (no new relation should be generated). + CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->Connect(source_data_1, dest_1); + CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + + auto relUID = rule->GetRelationUIDs(source_data_1, dest_1); + CPPUNIT_ASSERT(relUID.size() == 1); + + std::string name = "MITK.Relations.1.destinationUID"; + auto prop = source_data_1->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE( + "Destination uid was not stored with the correct key. Already existing session should be used.", prop); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); + + name = "MITK.Relations.1.ruleID"; + prop = source_data_1->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); + + name = "MITK.Relations.1.SourceImageSequenceItem"; + prop = source_data_1->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); + + dcmRefs = GetReferenceSequenceIndices(source_data_1, dest_1); + CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_data_1, "dest_1", "image", "Test", 0)); + + // check actualization of an id only connection + rule->Connect(source_idOnly_1, dest_1); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == + mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", + rule->GetExistingRelations(source_1).size() == 1); + + // check actualization of an existing connection + rule->Connect(source_1, dest_1); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", + rule->GetExistingRelations(source_1).size() == 1); + name = "MITK.Relations.1.destinationUID"; + prop = source_1->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE( + "Destination uid was not stored with the correct key. Already existing session should be used.", prop); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); + + name = "MITK.Relations.1.ruleID"; + prop = source_1->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); + + name = "MITK.Relations.1.SourceImageSequenceItem"; + prop = source_1->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); + + dcmRefs = GetReferenceSequenceIndices(source_1, dest_1); + CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); + + // check creation of an new connection + rule->Connect(unRelated, dest_1); + CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT_MESSAGE("Relation was not defined instead of updating exting one.", + rule->GetExistingRelations(unRelated).size() == 1); + name = "MITK.Relations.1.destinationUID"; + prop = unRelated->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); + + name = "MITK.Relations.1.ruleID"; + prop = unRelated->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); + + name = "MITK.Relations.1.SourceImageSequenceItem"; + prop = unRelated->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); + + dcmRefs = GetReferenceSequenceIndices(unRelated, dest_1); + CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); + CPPUNIT_ASSERT_MESSAGE("Dicom reference squence is corrupted. Should be just an index 0.", dcmRefs[0] == "0"); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); + + // check creation of a 2nd connection of the same purpose + rule->Connect(unRelated, dest_2); + CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT_MESSAGE("Additional relation was not defined.", + rule->GetExistingRelations(unRelated).size() == 2); + name = "MITK.Relations.1.destinationUID"; + prop = unRelated->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); + + name = "MITK.Relations.1.ruleID"; + prop = unRelated->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); + + name = "MITK.Relations.1.SourceImageSequenceItem"; + prop = unRelated->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); + + name = "MITK.Relations.2.destinationUID"; + prop = unRelated->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_2->GetUID()); + + name = "MITK.Relations.2.ruleID"; + prop = unRelated->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); + + name = "MITK.Relations.2.SourceImageSequenceItem"; + prop = unRelated->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "1"); + + + dcmRefs = GetReferenceSequenceIndices(unRelated, dest_2); + CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was not defined.", dcmRefs.size() == 1); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_2", "image", "Test", 1)); + } + + void Disconnect() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->Disconnect(nullptr, dest_1), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->Disconnect(source_1, nullptr), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->Disconnect(nullptr, "uid"), + itk::ExceptionObject); + + CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); + rule->Disconnect(source_1, unRelated); + CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT_MESSAGE("Other relationdata property was removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); + + //check if index correction is correct, when disconnecting + rule->Connect(source_1, dest_2); + rule->Connect(source_1, unRelated); + rule->Disconnect(source_1, dest_2); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "1")); + CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "2")); + CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "3")); + CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "4")); + CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_1 has been removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_2 (other purpose) has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "dest_2", "image", "otherpurpose", 1)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference to unRelated has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "unRelated", "image", "Test", 2)); + + std::string name = "MITK.Relations.4.destinationUID"; + auto prop = source_1->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE( + "Destination uid was not stored with the correct key. Already existing session should be used.", prop); + CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated->GetUID()); + name = "MITK.Relations.4.SourceImageSequenceItem"; + prop = source_1->GetProperty(name.c_str()); + CPPUNIT_ASSERT_MESSAGE("SourceImageSequenceItem was not actualized correctly.", prop->GetValueAsString() == "2"); + + rule->Disconnect(source_otherPurpose, dest_1); + CPPUNIT_ASSERT_MESSAGE("Data of other rule purpose was removed.", this->hasRelationProperties(source_otherPurpose, "1")); + } + + void Connect_abstract() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", + abstractRule->Connect(nullptr, dest_1), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", + abstractRule->Connect(source_1, nullptr), + itk::ExceptionObject); + } + + void Disconnect_abstract() + { + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + abstractRule->Disconnect(source_1, dest_2); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "2")); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + abstractRule->Disconnect(source_1, dest_1); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); + + abstractRule->Disconnect(source_otherPurpose, dest_1); + CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", !this->hasRelationProperties(source_otherPurpose, "1")); + } + +}; + +MITK_TEST_SUITE_REGISTRATION(mitkSourceImageRelationRule) diff --git a/Modules/DiffusionImaging/DiffusionCmdApps/FiberProcessing/FiberProcessing.cpp b/Modules/DiffusionImaging/DiffusionCmdApps/FiberProcessing/FiberProcessing.cpp index b3bf6d1373..6b3e3a59bf 100644 --- a/Modules/DiffusionImaging/DiffusionCmdApps/FiberProcessing/FiberProcessing.cpp +++ b/Modules/DiffusionImaging/DiffusionCmdApps/FiberProcessing/FiberProcessing.cpp @@ -1,259 +1,264 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include "mitkCommandLineParser.h" #include #include #include #include mitk::FiberBundle::Pointer LoadFib(std::string filename) { std::vector fibInfile = mitk::IOUtil::Load(filename); if( fibInfile.empty() ) std::cout << "File " << filename << " could not be read!"; mitk::BaseData::Pointer baseData = fibInfile.at(0); return dynamic_cast(baseData.GetPointer()); } /*! \brief Modify input tractogram: fiber resampling, compression, pruning and transformation. */ int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Fiber Processing"); parser.setCategory("Fiber Tracking and Processing Methods"); parser.setDescription("Modify input tractogram: fiber resampling, compression, pruning and transformation."); parser.setContributor("MIC"); parser.setArgumentPrefix("--", "-"); parser.beginGroup("1. Mandatory arguments:"); parser.addArgument("", "i", mitkCommandLineParser::String, "Input:", "Input fiber bundle (.fib, .trk, .tck)", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("", "o", mitkCommandLineParser::String, "Output:", "Output fiber bundle (.fib, .trk)", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.endGroup(); parser.beginGroup("2. Resampling:"); parser.addArgument("spline_resampling", "", mitkCommandLineParser::Float, "Spline resampling:", "Resample fiber using splines with the given point distance (in mm)"); parser.addArgument("linear_resampling", "", mitkCommandLineParser::Float, "Linear resampling:", "Resample fiber linearly with the given point distance (in mm)"); parser.addArgument("num_resampling", "", mitkCommandLineParser::Int, "Num. fiber points resampling:", "Resample all fibers to the given number of points"); parser.addArgument("compress", "", mitkCommandLineParser::Float, "Compress:", "Compress fiber using the given error threshold (in mm)"); parser.endGroup(); parser.beginGroup("3. Filtering:"); parser.addArgument("min_length", "", mitkCommandLineParser::Float, "Minimum length:", "Minimum fiber length (in mm)"); parser.addArgument("max_length", "", mitkCommandLineParser::Float, "Maximum length:", "Maximum fiber length (in mm)"); - parser.addArgument("max_angle", "", mitkCommandLineParser::Float, "Maximum angle:", "Maximum angular STDEV over 1cm (in degree)"); + parser.addArgument("max_angle", "", mitkCommandLineParser::Float, "Maximum angle:", "Maximum angular STDEV (in degree) over given distance"); + parser.addArgument("max_angle_dist", "", mitkCommandLineParser::Float, "Distance:", "Distance in mm", 10); parser.addArgument("remove", "", mitkCommandLineParser::Bool, "Remove fibers exceeding curvature threshold:", "If false, only the high curvature parts are removed"); parser.addArgument("subsample", "", mitkCommandLineParser::Float, "Randomly select fraction of streamlines:", "Randomly select the specified fraction of streamlines from the input tractogram"); parser.addArgument("random_subsample", "", mitkCommandLineParser::Bool, "Randomly seed subsampling:", "Randomly seed subsampling. Else, use seed 0."); parser.endGroup(); parser.beginGroup("4. Transformation:"); parser.addArgument("mirror", "", mitkCommandLineParser::Int, "Invert coordinates:", "Invert fiber coordinates XYZ (e.g. 010 to invert y-coordinate of each fiber point)"); parser.addArgument("rotate_x", "", mitkCommandLineParser::Float, "Rotate x-axis:", "Rotate around x-axis (in deg)"); parser.addArgument("rotate_y", "", mitkCommandLineParser::Float, "Rotate y-axis:", "Rotate around y-axis (in deg)"); parser.addArgument("rotate_z", "", mitkCommandLineParser::Float, "Rotate z-axis:", "Rotate around z-axis (in deg)"); parser.addArgument("scale_x", "", mitkCommandLineParser::Float, "Scale x-axis:", "Scale in direction of x-axis"); parser.addArgument("scale_y", "", mitkCommandLineParser::Float, "Scale y-axis:", "Scale in direction of y-axis"); parser.addArgument("scale_z", "", mitkCommandLineParser::Float, "Scale z-axis", "Scale in direction of z-axis"); parser.addArgument("translate_x", "", mitkCommandLineParser::Float, "Translate x-axis:", "Translate in direction of x-axis (in mm)"); parser.addArgument("translate_y", "", mitkCommandLineParser::Float, "Translate y-axis:", "Translate in direction of y-axis (in mm)"); parser.addArgument("translate_z", "", mitkCommandLineParser::Float, "Translate z-axis:", "Translate in direction of z-axis (in mm)"); parser.endGroup(); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; bool remove = false; if (parsedArgs.count("remove")) remove = us::any_cast(parsedArgs["remove"]); bool random_subsample = false; if (parsedArgs.count("random_subsample")) random_subsample = us::any_cast(parsedArgs["random_subsample"]); float spline_resampling = -1; if (parsedArgs.count("spline_resampling")) spline_resampling = us::any_cast(parsedArgs["spline_resampling"]); float linear_resampling = -1; if (parsedArgs.count("linear_resampling")) linear_resampling = us::any_cast(parsedArgs["linear_resampling"]); int num_resampling = -1; if (parsedArgs.count("num_resampling")) num_resampling = us::any_cast(parsedArgs["num_resampling"]); float subsample = -1; if (parsedArgs.count("subsample")) subsample = us::any_cast(parsedArgs["subsample"]); float compress = -1; if (parsedArgs.count("compress")) compress = us::any_cast(parsedArgs["compress"]); float minFiberLength = -1; if (parsedArgs.count("min_length")) minFiberLength = us::any_cast(parsedArgs["min_length"]); float maxFiberLength = -1; if (parsedArgs.count("max_length")) maxFiberLength = us::any_cast(parsedArgs["max_length"]); + float max_angle_dist = 10; + if (parsedArgs.count("max_angle_dist")) + max_angle_dist = us::any_cast(parsedArgs["max_angle_dist"]); + float maxAngularDev = -1; if (parsedArgs.count("max_angle")) maxAngularDev = us::any_cast(parsedArgs["max_angle"]); int axis = 0; if (parsedArgs.count("mirror")) axis = us::any_cast(parsedArgs["mirror"]); float rotateX = 0; if (parsedArgs.count("rotate_x")) rotateX = us::any_cast(parsedArgs["rotate_x"]); float rotateY = 0; if (parsedArgs.count("rotate_y")) rotateY = us::any_cast(parsedArgs["rotate_y"]); float rotateZ = 0; if (parsedArgs.count("rotate_z")) rotateZ = us::any_cast(parsedArgs["rotate_z"]); float scaleX = 0; if (parsedArgs.count("scale_x")) scaleX = us::any_cast(parsedArgs["scale_x"]); float scaleY = 0; if (parsedArgs.count("scale_y")) scaleY = us::any_cast(parsedArgs["scale_y"]); float scaleZ = 0; if (parsedArgs.count("scale_z")) scaleZ = us::any_cast(parsedArgs["scale_z"]); float translateX = 0; if (parsedArgs.count("translate_x")) translateX = us::any_cast(parsedArgs["translate_x"]); float translateY = 0; if (parsedArgs.count("translate_y")) translateY = us::any_cast(parsedArgs["translate_y"]); float translateZ = 0; if (parsedArgs.count("translate_z")) translateZ = us::any_cast(parsedArgs["translate_z"]); std::string inFileName = us::any_cast(parsedArgs["i"]); std::string outFileName = us::any_cast(parsedArgs["o"]); try { mitk::FiberBundle::Pointer fib = LoadFib(inFileName); if (subsample>0) fib = fib->SubsampleFibers(subsample, random_subsample); if (maxAngularDev>0) { auto filter = itk::FiberCurvatureFilter::New(); filter->SetInputFiberBundle(fib); filter->SetAngularDeviation(maxAngularDev); - filter->SetDistance(10); + filter->SetDistance(max_angle_dist); filter->SetRemoveFibers(remove); filter->Update(); fib = filter->GetOutputFiberBundle(); } if (minFiberLength>0) fib->RemoveShortFibers(minFiberLength); if (maxFiberLength>0) fib->RemoveLongFibers(maxFiberLength); if (spline_resampling>0) fib->ResampleSpline(spline_resampling); if (linear_resampling>0) fib->ResampleLinear(linear_resampling); if (num_resampling>0) fib->ResampleToNumPoints(num_resampling); if (compress>0) fib->Compress(compress); if (axis/100==1) fib->MirrorFibers(0); if ((axis%100)/10==1) fib->MirrorFibers(1); if (axis%10==1) fib->MirrorFibers(2); if (rotateX > 0 || rotateY > 0 || rotateZ > 0){ std::cout << "Rotate " << rotateX << " " << rotateY << " " << rotateZ; fib->RotateAroundAxis(rotateX, rotateY, rotateZ); } if (translateX > 0 || translateY > 0 || translateZ > 0){ fib->TranslateFibers(translateX, translateY, translateZ); } if (scaleX > 0 || scaleY > 0 || scaleZ > 0) fib->ScaleFibers(scaleX, scaleY, scaleZ); mitk::IOUtil::Save(fib.GetPointer(), outFileName ); } catch (itk::ExceptionObject e) { std::cout << e; return EXIT_FAILURE; } catch (std::exception e) { std::cout << e.what(); return EXIT_FAILURE; } catch (...) { std::cout << "ERROR!?!"; return EXIT_FAILURE; } return EXIT_SUCCESS; } diff --git a/Modules/DiffusionImaging/DiffusionCmdApps/Fiberfox/Fiberfox.cpp b/Modules/DiffusionImaging/DiffusionCmdApps/Fiberfox/Fiberfox.cpp index f02878bbb6..534aef2528 100755 --- a/Modules/DiffusionImaging/DiffusionCmdApps/Fiberfox/Fiberfox.cpp +++ b/Modules/DiffusionImaging/DiffusionCmdApps/Fiberfox/Fiberfox.cpp @@ -1,263 +1,288 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include "mitkCommandLineParser.h" #include #include #include #include #include using namespace mitk; /*! * \brief Command line interface to Fiberfox. * Simulate a diffusion-weighted image from a tractogram using the specified parameter file. */ int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Fiberfox"); parser.setCategory("Diffusion Simulation Tools"); parser.setContributor("MIC"); parser.setDescription("Command line interface to Fiberfox." " Simulate a diffusion-weighted image from a tractogram using the specified parameter file."); parser.setArgumentPrefix("--", "-"); parser.addArgument("", "o", mitkCommandLineParser::String, "Output root:", "output folder and file prefix", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.addArgument("", "i", mitkCommandLineParser::String, "Input:", "input tractogram or diffusion-weighted image", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("parameters", "p", mitkCommandLineParser::String, "Parameter file:", "fiberfox parameter file (.ffp)", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("template", "t", mitkCommandLineParser::String, "Template image:", "use parameters of the template image", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument("verbose", "v", mitkCommandLineParser::Bool, "Output additional images:", "output volume fraction images etc.", us::Any()); parser.addArgument("dont_apply_direction_matrix", "", mitkCommandLineParser::Bool, "Don't apply direction matrix:", "don't rotate gradients by image direction matrix", us::Any()); parser.addArgument("fix_seed", "", mitkCommandLineParser::Bool, "Use fix random seed:", "always use same sequence of random numbers", us::Any()); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) { return EXIT_FAILURE; } std::string outName = us::any_cast(parsedArgs["o"]); std::string paramName = us::any_cast(parsedArgs["parameters"]); std::string input=""; if (parsedArgs.count("i")) input = us::any_cast(parsedArgs["i"]); bool fix_seed = false; if (parsedArgs.count("fix_seed")) fix_seed = us::any_cast(parsedArgs["fix_seed"]); bool verbose = false; if (parsedArgs.count("verbose")) verbose = us::any_cast(parsedArgs["verbose"]); bool apply_direction_matrix = true; if (parsedArgs.count("dont_apply_direction_matrix")) apply_direction_matrix = false; FiberfoxParameters parameters; parameters.LoadParameters(paramName, fix_seed); // Test if /path/dir is an existing directory: std::string file_extension = ""; if( itksys::SystemTools::FileIsDirectory( outName ) ) { while( *(--(outName.cend())) == '/') { outName.pop_back(); } outName = outName + '/'; parameters.m_Misc.m_OutputPath = outName; outName = outName + parameters.m_Misc.m_OutputPrefix; // using default m_OutputPrefix as initialized. } else { // outName is NOT an existing directory, so we need to remove all trailing slashes: while( *(--(outName.cend())) == '/') { outName.pop_back(); } // now split up the given outName into directory and (prefix of) filename: if( ! itksys::SystemTools::GetFilenamePath( outName ).empty() && itksys::SystemTools::FileIsDirectory(itksys::SystemTools::GetFilenamePath( outName ) ) ) { parameters.m_Misc.m_OutputPath = itksys::SystemTools::GetFilenamePath( outName ) + '/'; } else { parameters.m_Misc.m_OutputPath = itksys::SystemTools::GetCurrentWorkingDirectory() + '/'; } file_extension = itksys::SystemTools::GetFilenameExtension(outName); if( ! itksys::SystemTools::GetFilenameWithoutExtension( outName ).empty() ) { parameters.m_Misc.m_OutputPrefix = itksys::SystemTools::GetFilenameWithoutExtension( outName ); } else { parameters.m_Misc.m_OutputPrefix = "fiberfox"; } outName = parameters.m_Misc.m_OutputPath + parameters.m_Misc.m_OutputPrefix; } mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor({"Diffusion Weighted Images", "Fiberbundles"}, {}); mitk::BaseData::Pointer inputData = mitk::IOUtil::Load(input, &functor)[0]; itk::TractsToDWIImageFilter< short >::Pointer tractsToDwiFilter = itk::TractsToDWIImageFilter< short >::New(); if ( dynamic_cast(inputData.GetPointer()) ) // simulate dataset from fibers { tractsToDwiFilter->SetFiberBundle(dynamic_cast(inputData.GetPointer())); if (parsedArgs.count("template")) { MITK_INFO << "Loading template image"; typedef itk::VectorImage< short, 3 > ItkDwiType; typedef itk::Image< short, 3 > ItkImageType; mitk::BaseData::Pointer templateData = mitk::IOUtil::Load(us::any_cast(parsedArgs["template"]), &functor)[0]; mitk::Image::Pointer template_image = dynamic_cast(templateData.GetPointer()); if (mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(template_image)) { ItkDwiType::Pointer itkVectorImagePointer = mitk::DiffusionPropertyHelper::GetItkVectorImage(template_image); parameters.m_SignalGen.m_ImageRegion = itkVectorImagePointer->GetLargestPossibleRegion(); parameters.m_SignalGen.m_ImageSpacing = itkVectorImagePointer->GetSpacing(); parameters.m_SignalGen.m_ImageOrigin = itkVectorImagePointer->GetOrigin(); parameters.m_SignalGen.m_ImageDirection = itkVectorImagePointer->GetDirection(); parameters.SetBvalue(mitk::DiffusionPropertyHelper::GetReferenceBValue(template_image)); parameters.SetGradienDirections(mitk::DiffusionPropertyHelper::GetOriginalGradientContainer(template_image)); } else { ItkImageType::Pointer itkImagePointer = ItkImageType::New(); mitk::CastToItkImage(template_image, itkImagePointer); parameters.m_SignalGen.m_ImageRegion = itkImagePointer->GetLargestPossibleRegion(); parameters.m_SignalGen.m_ImageSpacing = itkImagePointer->GetSpacing(); parameters.m_SignalGen.m_ImageOrigin = itkImagePointer->GetOrigin(); parameters.m_SignalGen.m_ImageDirection = itkImagePointer->GetDirection(); } } } else if ( dynamic_cast(inputData.GetPointer()) ) // add artifacts to existing image { typedef itk::VectorImage< short, 3 > ItkDwiType; mitk::Image::Pointer diffImg = dynamic_cast(inputData.GetPointer()); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(diffImg, itkVectorImagePointer); parameters.m_SignalGen.m_SignalScale = 1; parameters.m_SignalGen.m_ImageRegion = itkVectorImagePointer->GetLargestPossibleRegion(); parameters.m_SignalGen.m_ImageSpacing = itkVectorImagePointer->GetSpacing(); parameters.m_SignalGen.m_ImageOrigin = itkVectorImagePointer->GetOrigin(); parameters.m_SignalGen.m_ImageDirection = itkVectorImagePointer->GetDirection(); parameters.SetBvalue(mitk::DiffusionPropertyHelper::GetReferenceBValue(diffImg)); parameters.SetGradienDirections(mitk::DiffusionPropertyHelper::GetOriginalGradientContainer(diffImg)); tractsToDwiFilter->SetInputImage(itkVectorImagePointer); } if (verbose) { MITK_DEBUG << outName << ".ffp"; + parameters.m_Misc.m_OutputAdditionalImages = true; parameters.SaveParameters(outName+".ffp"); } + else + parameters.m_Misc.m_OutputAdditionalImages = false; if (apply_direction_matrix) { MITK_INFO << "Applying direction matrix to gradient directions."; parameters.ApplyDirectionMatrix(); } tractsToDwiFilter->SetParameters(parameters); tractsToDwiFilter->SetUseConstantRandSeed(fix_seed); tractsToDwiFilter->Update(); mitk::Image::Pointer image = mitk::GrabItkImageMemory(tractsToDwiFilter->GetOutput()); - if (apply_direction_matrix) - mitk::DiffusionPropertyHelper::SetGradientContainer(image, parameters.m_SignalGen.GetItkGradientContainer()); - else - mitk::DiffusionPropertyHelper::SetOriginalGradientContainer(image, parameters.m_SignalGen.GetItkGradientContainer()); - mitk::DiffusionPropertyHelper::SetReferenceBValue(image, parameters.m_SignalGen.GetBvalue()); - mitk::DiffusionPropertyHelper::InitializeImage(image); - - if (file_extension=="") - mitk::IOUtil::Save(image, "DWI_NIFTI", outName+".nii.gz"); - else if (file_extension==".nii" || file_extension==".nii.gz") - mitk::IOUtil::Save(image, "DWI_NIFTI", outName+file_extension); + + if (parameters.m_SignalGen.GetNumWeightedVolumes()>0) + { + if (apply_direction_matrix) + mitk::DiffusionPropertyHelper::SetGradientContainer(image, parameters.m_SignalGen.GetItkGradientContainer()); + else + mitk::DiffusionPropertyHelper::SetOriginalGradientContainer(image, parameters.m_SignalGen.GetItkGradientContainer()); + mitk::DiffusionPropertyHelper::SetReferenceBValue(image, parameters.m_SignalGen.GetBvalue()); + mitk::DiffusionPropertyHelper::InitializeImage(image); + + if (file_extension=="") + mitk::IOUtil::Save(image, "DWI_NIFTI", outName+".nii.gz"); + else if (file_extension==".nii" || file_extension==".nii.gz") + mitk::IOUtil::Save(image, "DWI_NIFTI", outName+file_extension); + else + mitk::IOUtil::Save(image, outName+file_extension); + } else - mitk::IOUtil::Save(image, outName+file_extension); + mitk::IOUtil::Save(image, outName+".nii.gz"); if (verbose) { + if (tractsToDwiFilter->GetTickImage().IsNotNull()) + { + mitk::Image::Pointer mitkImage = mitk::Image::New(); + itk::TractsToDWIImageFilter< short >::Float2DImageType::Pointer itkImage = tractsToDwiFilter->GetTickImage(); + mitkImage = mitk::GrabItkImageMemory( itkImage.GetPointer() ); + mitk::IOUtil::Save(mitkImage, outName+"_Ticks.nii.gz"); + } + + if (tractsToDwiFilter->GetRfImage().IsNotNull()) + { + mitk::Image::Pointer mitkImage = mitk::Image::New(); + itk::TractsToDWIImageFilter< short >::Float2DImageType::Pointer itkImage = tractsToDwiFilter->GetRfImage(); + mitkImage = mitk::GrabItkImageMemory( itkImage.GetPointer() ); + mitk::IOUtil::Save(mitkImage, outName+"_TimeFromRf.nii.gz"); + } + std::vector< itk::TractsToDWIImageFilter< short >::ItkDoubleImgType::Pointer > volumeFractions = tractsToDwiFilter->GetVolumeFractions(); for (unsigned int k=0; kInitializeByItk(volumeFractions.at(k).GetPointer()); image->SetVolume(volumeFractions.at(k)->GetBufferPointer()); mitk::IOUtil::Save(image, outName+"_Compartment"+boost::lexical_cast(k+1)+".nii.gz"); } if (tractsToDwiFilter->GetPhaseImage().IsNotNull()) { mitk::Image::Pointer image = mitk::Image::New(); itk::TractsToDWIImageFilter< short >::DoubleDwiType::Pointer itkPhase = tractsToDwiFilter->GetPhaseImage(); image = mitk::GrabItkImageMemory( itkPhase.GetPointer() ); mitk::IOUtil::Save(image, outName+"_Phase.nii.gz"); } if (tractsToDwiFilter->GetKspaceImage().IsNotNull()) { mitk::Image::Pointer image = mitk::Image::New(); itk::TractsToDWIImageFilter< short >::DoubleDwiType::Pointer itkImage = tractsToDwiFilter->GetKspaceImage(); image = mitk::GrabItkImageMemory( itkImage.GetPointer() ); mitk::IOUtil::Save(image, outName+"_kSpace.nii.gz"); } int c = 1; std::vector< itk::TractsToDWIImageFilter< short >::DoubleDwiType::Pointer > output_real = tractsToDwiFilter->GetOutputImagesReal(); for (auto real : output_real) { mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(real.GetPointer()); image->SetVolume(real->GetBufferPointer()); mitk::IOUtil::Save(image, outName+"_Coil-"+boost::lexical_cast(c)+"-real.nii.gz"); ++c; } c = 1; std::vector< itk::TractsToDWIImageFilter< short >::DoubleDwiType::Pointer > output_imag = tractsToDwiFilter->GetOutputImagesImag(); for (auto imag : output_imag) { mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(imag.GetPointer()); image->SetVolume(imag->GetBufferPointer()); mitk::IOUtil::Save(image, outName+"_Coil-"+boost::lexical_cast(c)+"-imag.nii.gz"); ++c; } } return EXIT_SUCCESS; } diff --git a/Modules/DiffusionImaging/DiffusionCmdApps/Tractography/StreamlineTractography.cpp b/Modules/DiffusionImaging/DiffusionCmdApps/Tractography/StreamlineTractography.cpp index fea9c341cf..e5f986b2f0 100755 --- a/Modules/DiffusionImaging/DiffusionCmdApps/Tractography/StreamlineTractography.cpp +++ b/Modules/DiffusionImaging/DiffusionCmdApps/Tractography/StreamlineTractography.cpp @@ -1,568 +1,566 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #define _USE_MATH_DEFINES #include const int numOdfSamples = 200; typedef itk::Image< itk::Vector< float, numOdfSamples > , 3 > SampledShImageType; /*! \brief */ int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Streamline Tractography"); parser.setCategory("Fiber Tracking and Processing Methods"); parser.setDescription("Perform streamline tractography"); parser.setContributor("MIC"); // parameters fo all methods parser.setArgumentPrefix("--", "-"); parser.beginGroup("1. Mandatory arguments:"); parser.addArgument("", "i", mitkCommandLineParser::StringList, "Input:", "input image (multiple possible for 'DetTensor' algorithm)", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("", "o", mitkCommandLineParser::String, "Output:", "output fiberbundle/probability map", us::Any(), false, false, false, mitkCommandLineParser::Output); - parser.addArgument("algorithm", "", mitkCommandLineParser::String, "Algorithm:", "which algorithm to use (Peaks; DetTensor; ProbTensor; DetODF; ProbODF; DetRF; ProbRF)", us::Any(), false); + parser.addArgument("type", "", mitkCommandLineParser::String, "Type:", "which tracker to use (Peaks; Tensor; ODF; RF)", us::Any(), false); + parser.addArgument("probabilistic", "", mitkCommandLineParser::Bool, "Probabilistic:", "Probabilistic tractography", us::Any(false)); parser.endGroup(); parser.beginGroup("2. Seeding:"); parser.addArgument("seeds", "", mitkCommandLineParser::Int, "Seeds per voxel:", "number of seed points per voxel", 1); parser.addArgument("seed_image", "", mitkCommandLineParser::String, "Seed image:", "mask image defining seed voxels", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument("trials_per_seed", "", mitkCommandLineParser::Int, "Max. trials per seed:", "try each seed N times until a valid streamline is obtained (only for probabilistic tractography)", 10); parser.addArgument("max_tracts", "", mitkCommandLineParser::Int, "Max. number of tracts:", "tractography is stopped if the reconstructed number of tracts is exceeded", -1); parser.endGroup(); parser.beginGroup("3. Tractography constraints:"); parser.addArgument("tracking_mask", "", mitkCommandLineParser::String, "Mask image:", "streamlines leaving the mask will stop immediately", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument("stop_image", "", mitkCommandLineParser::String, "Stop ROI image:", "streamlines entering the mask will stop immediately", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument("exclusion_image", "", mitkCommandLineParser::String, "Exclusion ROI image:", "streamlines entering the mask will be discarded", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument("ep_constraint", "", mitkCommandLineParser::String, "Endpoint constraint:", "determines which fibers are accepted based on their endpoint location - options are NONE, EPS_IN_TARGET, EPS_IN_TARGET_LABELDIFF, EPS_IN_SEED_AND_TARGET, MIN_ONE_EP_IN_TARGET, ONE_EP_IN_TARGET and NO_EP_IN_TARGET", us::Any()); parser.addArgument("target_image", "", mitkCommandLineParser::String, "Target ROI image:", "effact depends on the chosen endpoint constraint (option ep_constraint)", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.endGroup(); parser.beginGroup("4. Streamline integration parameters:"); parser.addArgument("sharpen_odfs", "", mitkCommandLineParser::Bool, "SHarpen ODFs:", "if you are using dODF images as input, it is advisable to sharpen the ODFs (min-max normalize and raise to the power of 4). this is not necessary for CSD fODFs, since they are narurally much sharper."); parser.addArgument("cutoff", "", mitkCommandLineParser::Float, "Cutoff:", "set the FA, GFA or Peak amplitude cutoff for terminating tracks", 0.1); parser.addArgument("odf_cutoff", "", mitkCommandLineParser::Float, "ODF Cutoff:", "threshold on the ODF magnitude. this is useful in case of CSD fODF tractography.", 0.0); parser.addArgument("step_size", "", mitkCommandLineParser::Float, "Step size:", "step size (in voxels)", 0.5); parser.addArgument("min_tract_length", "", mitkCommandLineParser::Float, "Min. tract length:", "minimum fiber length (in mm)", 20); parser.addArgument("angular_threshold", "", mitkCommandLineParser::Float, "Angular threshold:", "angular threshold between two successive steps, (default: 90° * step_size, minimum 15°)"); parser.addArgument("loop_check", "", mitkCommandLineParser::Float, "Check for loops:", "threshold on angular stdev over the last 4 voxel lengths"); + parser.addArgument("peak_jitter", "", mitkCommandLineParser::Float, "Peak jitter:", "important for probabilistic peak tractography and peak prior. actual jitter is drawn from a normal distribution with peak_jitter*fabs(direction_value) as standard deviation.", 0.01); parser.endGroup(); parser.beginGroup("5. Tractography prior:"); parser.addArgument("prior_image", "", mitkCommandLineParser::String, "Peak prior:", "tractography prior in thr for of a peak image", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument("prior_weight", "", mitkCommandLineParser::Float, "Prior weight", "weighting factor between prior and data.", 0.5); - parser.addArgument("restrict_to_prior", "", mitkCommandLineParser::Bool, "Restrict to prior:", "restrict tractography to regions where the prior is valid."); - parser.addArgument("new_directions_from_prior", "", mitkCommandLineParser::Bool, "New directios from prior:", "the prior can create directions where there are none in the data."); + parser.addArgument("dont_restrict_to_prior", "", mitkCommandLineParser::Bool, "Don't restrict to prior:", "don't restrict tractography to regions where the prior is valid.", us::Any(false)); + parser.addArgument("no_new_directions_from_prior", "", mitkCommandLineParser::Bool, "No new directios from prior:", "the prior cannot create directions where there are none in the data.", us::Any(false)); + parser.addArgument("prior_flip_x", "", mitkCommandLineParser::Bool, "Prior Flip X:", "multiply x-coordinate of prior direction by -1"); + parser.addArgument("prior_flip_y", "", mitkCommandLineParser::Bool, "Prior Flip Y:", "multiply y-coordinate of prior direction by -1"); + parser.addArgument("prior_flip_z", "", mitkCommandLineParser::Bool, "Prior Flip Z:", "multiply z-coordinate of prior direction by -1"); parser.endGroup(); parser.beginGroup("6. Neighborhood sampling:"); parser.addArgument("num_samples", "", mitkCommandLineParser::Int, "Num. neighborhood samples:", "number of neighborhood samples that are use to determine the next progression direction", 0); parser.addArgument("sampling_distance", "", mitkCommandLineParser::Float, "Sampling distance:", "distance of neighborhood sampling points (in voxels)", 0.25); parser.addArgument("use_stop_votes", "", mitkCommandLineParser::Bool, "Use stop votes:", "use stop votes"); parser.addArgument("use_only_forward_samples", "", mitkCommandLineParser::Bool, "Use only forward samples:", "use only forward samples"); parser.endGroup(); parser.beginGroup("7. Tensor tractography specific:"); parser.addArgument("tend_f", "", mitkCommandLineParser::Float, "Weight f", "weighting factor between first eigenvector (f=1 equals FACT tracking) and input vector dependent direction (f=0).", 1.0); parser.addArgument("tend_g", "", mitkCommandLineParser::Float, "Weight g", "weighting factor between input vector (g=0) and tensor deflection (g=1 equals TEND tracking)", 0.0); parser.endGroup(); parser.beginGroup("8. Random forest tractography specific:"); parser.addArgument("forest", "", mitkCommandLineParser::String, "Forest:", "input random forest (HDF5 file)", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument("use_sh_features", "", mitkCommandLineParser::Bool, "Use SH features:", "use SH features"); parser.endGroup(); parser.beginGroup("9. Additional input:"); parser.addArgument("additional_images", "", mitkCommandLineParser::StringList, "Additional images:", "specify a list of float images that hold additional information (FA, GFA, additional features for RF tractography)", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.endGroup(); parser.beginGroup("10. Misc:"); parser.addArgument("flip_x", "", mitkCommandLineParser::Bool, "Flip X:", "multiply x-coordinate of direction proposal by -1"); parser.addArgument("flip_y", "", mitkCommandLineParser::Bool, "Flip Y:", "multiply y-coordinate of direction proposal by -1"); parser.addArgument("flip_z", "", mitkCommandLineParser::Bool, "Flip Z:", "multiply z-coordinate of direction proposal by -1"); parser.addArgument("no_data_interpolation", "", mitkCommandLineParser::Bool, "Don't interpolate input data:", "don't interpolate input image values"); parser.addArgument("no_mask_interpolation", "", mitkCommandLineParser::Bool, "Don't interpolate masks:", "don't interpolate mask image values"); parser.addArgument("compress", "", mitkCommandLineParser::Float, "Compress:", "compress output fibers using the given error threshold (in mm)"); parser.addArgument("fix_seed", "", mitkCommandLineParser::Bool, "Fix Random Seed:", "always use the same random numbers"); + parser.addArgument("parameter_file", "", mitkCommandLineParser::String, "Parameter File:", "load parameters from json file (svae using MITK Diffusion GUI). the parameters loaded form this file are overwritten by the manually set parameters.", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.endGroup(); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; mitkCommandLineParser::StringContainerType input_files = us::any_cast(parsedArgs["i"]); std::string outFile = us::any_cast(parsedArgs["o"]); - std::string algorithm = us::any_cast(parsedArgs["algorithm"]); + std::string type = us::any_cast(parsedArgs["type"]); + + std::shared_ptr< mitk::StreamlineTractographyParameters > params = std::make_shared(); + + if (parsedArgs.count("parameter_file")) + { + auto parameter_file = us::any_cast(parsedArgs["parameter_file"]); + params->LoadParameters(parameter_file); + } + + if (parsedArgs.count("probabilistic")) + params->m_Mode = mitk::StreamlineTractographyParameters::MODE::PROBABILISTIC; + else { + params->m_Mode = mitk::StreamlineTractographyParameters::MODE::DETERMINISTIC; + } std::string prior_image = ""; if (parsedArgs.count("prior_image")) prior_image = us::any_cast(parsedArgs["prior_image"]); - float prior_weight = 0.5; if (parsedArgs.count("prior_weight")) - prior_weight = us::any_cast(parsedArgs["prior_weight"]); + params->m_Weight = us::any_cast(parsedArgs["prior_weight"]); - bool fix_seed = false; if (parsedArgs.count("fix_seed")) - fix_seed = us::any_cast(parsedArgs["fix_seed"]); + params->m_FixRandomSeed = us::any_cast(parsedArgs["fix_seed"]); - bool restrict_to_prior = false; - if (parsedArgs.count("restrict_to_prior")) - restrict_to_prior = us::any_cast(parsedArgs["restrict_to_prior"]); + params->m_RestrictToPrior = true; + if (parsedArgs.count("dont_restrict_to_prior")) + params->m_RestrictToPrior = !us::any_cast(parsedArgs["dont_restrict_to_prior"]); - bool new_directions_from_prior = false; - if (parsedArgs.count("new_directions_from_prior")) - new_directions_from_prior = us::any_cast(parsedArgs["new_directions_from_prior"]); + params->m_NewDirectionsFromPrior = true; + if (parsedArgs.count("no_new_directions_from_prior")) + params->m_NewDirectionsFromPrior = !us::any_cast(parsedArgs["no_new_directions_from_prior"]); - bool sharpen_odfs = false; + params->m_SharpenOdfs = false; if (parsedArgs.count("sharpen_odfs")) - sharpen_odfs = us::any_cast(parsedArgs["sharpen_odfs"]); + params->m_SharpenOdfs = us::any_cast(parsedArgs["sharpen_odfs"]); - bool interpolate = true; + params->m_InterpolateTractographyData = true; if (parsedArgs.count("no_data_interpolation")) - interpolate = !us::any_cast(parsedArgs["no_data_interpolation"]); + params->m_InterpolateTractographyData = !us::any_cast(parsedArgs["no_data_interpolation"]); - bool mask_interpolation = true; + params->m_InterpolateRoiImages = true; if (parsedArgs.count("no_mask_interpolation")) - interpolate = !us::any_cast(parsedArgs["no_mask_interpolation"]); + params->m_InterpolateRoiImages = !us::any_cast(parsedArgs["no_mask_interpolation"]); bool use_sh_features = false; if (parsedArgs.count("use_sh_features")) use_sh_features = us::any_cast(parsedArgs["use_sh_features"]); - bool use_stop_votes = false; + params->m_StopVotes = false; if (parsedArgs.count("use_stop_votes")) - use_stop_votes = us::any_cast(parsedArgs["use_stop_votes"]); + params->m_StopVotes = us::any_cast(parsedArgs["use_stop_votes"]); - bool use_only_forward_samples = false; + params->m_OnlyForwardSamples = false; if (parsedArgs.count("use_only_forward_samples")) - use_only_forward_samples = us::any_cast(parsedArgs["use_only_forward_samples"]); + params->m_OnlyForwardSamples = us::any_cast(parsedArgs["use_only_forward_samples"]); - bool flip_x = false; + params->m_FlipX = false; if (parsedArgs.count("flip_x")) - flip_x = us::any_cast(parsedArgs["flip_x"]); - bool flip_y = false; + params->m_FlipX = us::any_cast(parsedArgs["flip_x"]); + params->m_FlipY = false; if (parsedArgs.count("flip_y")) - flip_y = us::any_cast(parsedArgs["flip_y"]); - bool flip_z = false; + params->m_FlipY = us::any_cast(parsedArgs["flip_y"]); + params->m_FlipZ = false; if (parsedArgs.count("flip_z")) - flip_z = us::any_cast(parsedArgs["flip_z"]); - - bool apply_image_rotation = false; + params->m_FlipZ = us::any_cast(parsedArgs["flip_z"]); + + bool prior_flip_x = false; + if (parsedArgs.count("prior_flip_x")) + prior_flip_x = us::any_cast(parsedArgs["prior_flip_x"]); + bool prior_flip_y = false; + if (parsedArgs.count("prior_flip_y")) + prior_flip_y = us::any_cast(parsedArgs["prior_flip_y"]); + bool prior_flip_z = false; + if (parsedArgs.count("prior_flip_z")) + prior_flip_z = us::any_cast(parsedArgs["prior_flip_z"]); + + params->m_ApplyDirectionMatrix = false; if (parsedArgs.count("apply_image_rotation")) - apply_image_rotation = us::any_cast(parsedArgs["apply_image_rotation"]); + params->m_ApplyDirectionMatrix = us::any_cast(parsedArgs["apply_image_rotation"]); float compress = -1; if (parsedArgs.count("compress")) compress = us::any_cast(parsedArgs["compress"]); - float min_tract_length = 20; + params->m_MinTractLengthMm = 20; if (parsedArgs.count("min_tract_length")) - min_tract_length = us::any_cast(parsedArgs["min_tract_length"]); + params->m_MinTractLengthMm = us::any_cast(parsedArgs["min_tract_length"]); - float loop_check = -1; + params->SetLoopCheckDeg(-1); if (parsedArgs.count("loop_check")) - loop_check = us::any_cast(parsedArgs["loop_check"]); + params->SetLoopCheckDeg(us::any_cast(parsedArgs["loop_check"])); std::string forestFile; if (parsedArgs.count("forest")) forestFile = us::any_cast(parsedArgs["forest"]); std::string maskFile = ""; if (parsedArgs.count("tracking_mask")) maskFile = us::any_cast(parsedArgs["tracking_mask"]); std::string seedFile = ""; if (parsedArgs.count("seed_image")) seedFile = us::any_cast(parsedArgs["seed_image"]); std::string targetFile = ""; if (parsedArgs.count("target_image")) targetFile = us::any_cast(parsedArgs["target_image"]); std::string exclusionFile = ""; if (parsedArgs.count("exclusion_image")) exclusionFile = us::any_cast(parsedArgs["exclusion_image"]); std::string stopFile = ""; if (parsedArgs.count("stop_image")) stopFile = us::any_cast(parsedArgs["stop_image"]); std::string ep_constraint = "NONE"; if (parsedArgs.count("ep_constraint")) ep_constraint = us::any_cast(parsedArgs["ep_constraint"]); - float cutoff = 0.1f; + params->m_Cutoff = 0.1f; if (parsedArgs.count("cutoff")) - cutoff = us::any_cast(parsedArgs["cutoff"]); + params->m_Cutoff = us::any_cast(parsedArgs["cutoff"]); - float odf_cutoff = 0.0; + params->m_OdfCutoff = 0.0; if (parsedArgs.count("odf_cutoff")) - odf_cutoff = us::any_cast(parsedArgs["odf_cutoff"]); + params->m_OdfCutoff = us::any_cast(parsedArgs["odf_cutoff"]); - float stepsize = -1; + params->m_PeakJitter = 0.01; + if (parsedArgs.count("peak_jitter")) + params->m_PeakJitter = us::any_cast(parsedArgs["peak_jitter"]); + + params->SetStepSizeVox(-1); if (parsedArgs.count("step_size")) - stepsize = us::any_cast(parsedArgs["step_size"]); + params->SetStepSizeVox(us::any_cast(parsedArgs["step_size"])); - float sampling_distance = -1; + params->SetSamplingDistanceVox(-1); if (parsedArgs.count("sampling_distance")) - sampling_distance = us::any_cast(parsedArgs["sampling_distance"]); + params->SetSamplingDistanceVox(us::any_cast(parsedArgs["sampling_distance"])); - unsigned int num_samples = 0; + params->m_NumSamples = 0; if (parsedArgs.count("num_samples")) - num_samples = static_cast(us::any_cast(parsedArgs["num_samples"])); + params->m_NumSamples = static_cast(us::any_cast(parsedArgs["num_samples"])); - int num_seeds = 1; + params->m_SeedsPerVoxel = 1; if (parsedArgs.count("seeds")) - num_seeds = us::any_cast(parsedArgs["seeds"]); + params->m_SeedsPerVoxel = us::any_cast(parsedArgs["seeds"]); - unsigned int trials_per_seed = 10; + params->m_TrialsPerSeed = 10; if (parsedArgs.count("trials_per_seed")) - trials_per_seed = static_cast(us::any_cast(parsedArgs["trials_per_seed"])); + params->m_TrialsPerSeed = static_cast(us::any_cast(parsedArgs["trials_per_seed"])); - float tend_f = 1; + params->m_F = 1; if (parsedArgs.count("tend_f")) - tend_f = us::any_cast(parsedArgs["tend_f"]); + params->m_F = us::any_cast(parsedArgs["tend_f"]); - float tend_g = 0; + params->m_G = 0; if (parsedArgs.count("tend_g")) - tend_g = us::any_cast(parsedArgs["tend_g"]); + params->m_G = us::any_cast(parsedArgs["tend_g"]); - float angular_threshold = -1; + params->SetAngularThresholdDeg(-1); if (parsedArgs.count("angular_threshold")) - angular_threshold = us::any_cast(parsedArgs["angular_threshold"]); + params->SetAngularThresholdDeg(us::any_cast(parsedArgs["angular_threshold"])); - int max_tracts = -1; + params->m_MaxNumFibers = -1; if (parsedArgs.count("max_tracts")) - max_tracts = us::any_cast(parsedArgs["max_tracts"]); + params->m_MaxNumFibers = us::any_cast(parsedArgs["max_tracts"]); std::string ext = itksys::SystemTools::GetFilenameExtension(outFile); if (ext != ".fib" && ext != ".trk") { MITK_INFO << "Output file format not supported. Use one of .fib, .trk, .nii, .nii.gz, .nrrd"; return EXIT_FAILURE; } // LOAD DATASETS mitkCommandLineParser::StringContainerType addFiles; if (parsedArgs.count("additional_images")) addFiles = us::any_cast(parsedArgs["additional_images"]); typedef itk::Image ItkFloatImgType; ItkFloatImgType::Pointer mask = nullptr; if (!maskFile.empty()) { MITK_INFO << "loading mask image"; mitk::Image::Pointer img = mitk::IOUtil::Load(maskFile); mask = ItkFloatImgType::New(); mitk::CastToItkImage(img, mask); } ItkFloatImgType::Pointer seed = nullptr; if (!seedFile.empty()) { MITK_INFO << "loading seed ROI image"; mitk::Image::Pointer img = mitk::IOUtil::Load(seedFile); seed = ItkFloatImgType::New(); mitk::CastToItkImage(img, seed); } ItkFloatImgType::Pointer stop = nullptr; if (!stopFile.empty()) { MITK_INFO << "loading stop ROI image"; mitk::Image::Pointer img = mitk::IOUtil::Load(stopFile); stop = ItkFloatImgType::New(); mitk::CastToItkImage(img, stop); } ItkFloatImgType::Pointer target = nullptr; if (!targetFile.empty()) { MITK_INFO << "loading target ROI image"; mitk::Image::Pointer img = mitk::IOUtil::Load(targetFile); target = ItkFloatImgType::New(); mitk::CastToItkImage(img, target); } ItkFloatImgType::Pointer exclusion = nullptr; if (!exclusionFile.empty()) { MITK_INFO << "loading exclusion ROI image"; mitk::Image::Pointer img = mitk::IOUtil::Load(exclusionFile); exclusion = ItkFloatImgType::New(); mitk::CastToItkImage(img, exclusion); } MITK_INFO << "loading additional images"; std::vector< std::vector< ItkFloatImgType::Pointer > > addImages; addImages.push_back(std::vector< ItkFloatImgType::Pointer >()); for (auto file : addFiles) { mitk::Image::Pointer img = mitk::IOUtil::Load(file); ItkFloatImgType::Pointer itkimg = ItkFloatImgType::New(); mitk::CastToItkImage(img, itkimg); addImages.at(0).push_back(itkimg); } // ////////////////////////////////////////////////////////////////// // omp_set_num_threads(1); typedef itk::StreamlineTrackingFilter TrackerType; TrackerType::Pointer tracker = TrackerType::New(); if (!prior_image.empty()) { mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor({"Peak Image"}, {}); mitk::PeakImage::Pointer priorImage = mitk::IOUtil::Load(prior_image, &functor); if (priorImage.IsNull()) { MITK_INFO << "Only peak images are supported as prior at the moment!"; return EXIT_FAILURE; } mitk::TrackingDataHandler* priorhandler = new mitk::TrackingHandlerPeaks(); typedef mitk::ImageToItk< mitk::TrackingHandlerPeaks::PeakImgType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(priorImage); caster->Update(); mitk::TrackingHandlerPeaks::PeakImgType::Pointer itkImg = caster->GetOutput(); + std::shared_ptr< mitk::StreamlineTractographyParameters > prior_params = std::make_shared< mitk::StreamlineTractographyParameters >(*params); + prior_params->m_FlipX = prior_flip_x; + prior_params->m_FlipY = prior_flip_y; + prior_params->m_FlipZ = prior_flip_z; + prior_params->m_Cutoff = 0.0; dynamic_cast(priorhandler)->SetPeakImage(itkImg); - dynamic_cast(priorhandler)->SetPeakThreshold(0.0); - dynamic_cast(priorhandler)->SetInterpolate(interpolate); - dynamic_cast(priorhandler)->SetMode(mitk::TrackingDataHandler::MODE::DETERMINISTIC); - + priorhandler->SetParameters(prior_params); tracker->SetTrackingPriorHandler(priorhandler); - tracker->SetTrackingPriorWeight(prior_weight); - tracker->SetTrackingPriorAsMask(restrict_to_prior); - tracker->SetIntroduceDirectionsFromPrior(new_directions_from_prior); } mitk::TrackingDataHandler* handler; - if (algorithm == "DetRF" || algorithm == "ProbRF") + if (type == "RF") { mitk::TractographyForest::Pointer forest = mitk::IOUtil::Load(forestFile); if (forest.IsNull()) mitkThrow() << "Forest file " << forestFile << " could not be read."; mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor({"Diffusion Weighted Images"}, {}); auto input = mitk::IOUtil::Load(input_files.at(0), &functor); if (use_sh_features) { handler = new mitk::TrackingHandlerRandomForest<6,28>(); dynamic_cast*>(handler)->SetForest(forest); dynamic_cast*>(handler)->AddDwi(input); dynamic_cast*>(handler)->SetAdditionalFeatureImages(addImages); } else { handler = new mitk::TrackingHandlerRandomForest<6,100>(); dynamic_cast*>(handler)->SetForest(forest); dynamic_cast*>(handler)->AddDwi(input); dynamic_cast*>(handler)->SetAdditionalFeatureImages(addImages); } - - if (algorithm == "ProbRF") - handler->SetMode(mitk::TrackingDataHandler::MODE::PROBABILISTIC); } - else if (algorithm == "Peaks") + else if (type == "Peaks") { handler = new mitk::TrackingHandlerPeaks(); MITK_INFO << "loading input peak image"; mitk::Image::Pointer mitkImage = mitk::IOUtil::Load(input_files.at(0)); mitk::TrackingHandlerPeaks::PeakImgType::Pointer itkImg = mitk::convert::GetItkPeakFromPeakImage(mitkImage); - dynamic_cast(handler)->SetPeakImage(itkImg); - dynamic_cast(handler)->SetApplyDirectionMatrix(apply_image_rotation); - dynamic_cast(handler)->SetPeakThreshold(cutoff); } - else if (algorithm == "DetTensor") + else if (type == "Tensor" && params->m_Mode == mitk::StreamlineTractographyParameters::MODE::DETERMINISTIC) { handler = new mitk::TrackingHandlerTensor(); MITK_INFO << "loading input tensor images"; std::vector< mitk::Image::Pointer > input_images; for (unsigned int i=0; i(input_files.at(i)); mitk::TensorImage::ItkTensorImageType::Pointer itkImg = mitk::convert::GetItkTensorFromTensorImage(mitkImage); dynamic_cast(handler)->AddTensorImage(itkImg.GetPointer()); } - dynamic_cast(handler)->SetFaThreshold(cutoff); - dynamic_cast(handler)->SetF(tend_f); - dynamic_cast(handler)->SetG(tend_g); - if (addImages.at(0).size()>0) dynamic_cast(handler)->SetFaImage(addImages.at(0).at(0)); } - else if (algorithm == "DetODF" || algorithm == "ProbODF" || algorithm == "ProbTensor") + else if (type == "ODF" || (type == "Tensor" && params->m_Mode == mitk::StreamlineTractographyParameters::MODE::PROBABILISTIC)) { handler = new mitk::TrackingHandlerOdf(); mitk::OdfImage::ItkOdfImageType::Pointer itkImg = nullptr; - if (algorithm == "ProbTensor") + if (type == "Tensor") { MITK_INFO << "Converting Tensor to ODF image"; auto input = mitk::IOUtil::Load(input_files.at(0)); itkImg = mitk::convert::GetItkOdfFromTensorImage(input); - sharpen_odfs = true; - odf_cutoff = 0; dynamic_cast(handler)->SetIsOdfFromTensor(true); } else { mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor({"SH Image", "ODF Image"}, {}); auto input = mitk::IOUtil::Load(input_files.at(0), &functor)[0]; if (dynamic_cast(input.GetPointer())) { MITK_INFO << "Converting SH to ODF image"; mitk::Image::Pointer mitkImg = dynamic_cast(input.GetPointer()); itkImg = mitk::convert::GetItkOdfFromShImage(mitkImg); } else if (dynamic_cast(input.GetPointer())) { mitk::Image::Pointer mitkImg = dynamic_cast(input.GetPointer()); itkImg = mitk::convert::GetItkOdfFromOdfImage(mitkImg); } else mitkThrow() << ""; } dynamic_cast(handler)->SetOdfImage(itkImg); - dynamic_cast(handler)->SetGfaThreshold(cutoff); - dynamic_cast(handler)->SetOdfThreshold(odf_cutoff); - dynamic_cast(handler)->SetSharpenOdfs(sharpen_odfs); - - if (algorithm == "ProbODF" || algorithm == "ProbTensor") - dynamic_cast(handler)->SetMode(mitk::TrackingHandlerOdf::MODE::PROBABILISTIC); if (addImages.at(0).size()>0) dynamic_cast(handler)->SetGfaImage(addImages.at(0).at(0)); } else { - MITK_INFO << "Unknown tractography algorithm (" + algorithm+"). Known types are Peaks, DetTensor, ProbTensor, DetODF, ProbODF, DetRF, ProbRF."; + MITK_INFO << "Unknown tractography algorithm (" + type+"). Known types are Peaks, DetTensor, ProbTensor, DetODF, ProbODF, DetRF, ProbRF."; return EXIT_FAILURE; } - handler->SetInterpolate(interpolate); - handler->SetFlipX(flip_x); - handler->SetFlipY(flip_y); - handler->SetFlipZ(flip_z); if (ep_constraint=="NONE") - tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::NONE); + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::NONE; else if (ep_constraint=="EPS_IN_TARGET") - tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET); + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET; else if (ep_constraint=="EPS_IN_TARGET_LABELDIFF") - tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET_LABELDIFF); + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET_LABELDIFF; else if (ep_constraint=="EPS_IN_SEED_AND_TARGET") - tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_SEED_AND_TARGET); + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_SEED_AND_TARGET; else if (ep_constraint=="MIN_ONE_EP_IN_TARGET") - tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::MIN_ONE_EP_IN_TARGET); + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::MIN_ONE_EP_IN_TARGET; else if (ep_constraint=="ONE_EP_IN_TARGET") - tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::ONE_EP_IN_TARGET); + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::ONE_EP_IN_TARGET; else if (ep_constraint=="NO_EP_IN_TARGET") - tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::NO_EP_IN_TARGET); + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::NO_EP_IN_TARGET; - MITK_INFO << "Tractography algorithm: " << algorithm; - tracker->SetInterpolateMasks(mask_interpolation); - tracker->SetNumberOfSamples(num_samples); - tracker->SetAngularThreshold(angular_threshold); + MITK_INFO << "Tractography algorithm: " << type; tracker->SetMaskImage(mask); tracker->SetSeedImage(seed); tracker->SetStoppingRegions(stop); tracker->SetTargetRegions(target); tracker->SetExclusionRegions(exclusion); - tracker->SetSeedsPerVoxel(num_seeds); - tracker->SetStepSize(stepsize); - tracker->SetSamplingDistance(sampling_distance); - tracker->SetUseStopVotes(use_stop_votes); - tracker->SetOnlyForwardSamples(use_only_forward_samples); - tracker->SetLoopCheck(loop_check); - tracker->SetMaxNumTracts(max_tracts); - tracker->SetTrialsPerSeed(trials_per_seed); tracker->SetTrackingHandler(handler); if (ext != ".fib" && ext != ".trk") - tracker->SetUseOutputProbabilityMap(true); - tracker->SetMinTractLength(min_tract_length); - tracker->SetRandom(!fix_seed); + params->m_OutputProbMap = true; + tracker->SetParameters(params); tracker->Update(); if (ext == ".fib" || ext == ".trk") { vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); if (compress > 0) outFib->Compress(compress); mitk::IOUtil::Save(outFib, outFile); } else { TrackerType::ItkDoubleImgType::Pointer outImg = tracker->GetOutputProbabilityMap(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); if (ext != ".nii" && ext != ".nii.gz" && ext != ".nrrd") outFile += ".nii.gz"; mitk::IOUtil::Save(img, outFile); } delete handler; return EXIT_SUCCESS; } diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageDicomReaderService.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageDicomReaderService.cpp index ede4fc7775..a4e9350324 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageDicomReaderService.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageDicomReaderService.cpp @@ -1,326 +1,327 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkDiffusionImageDicomReaderService_cpp #define __mitkDiffusionImageDicomReaderService_cpp #include "mitkDiffusionImageDicomReaderService.h" #include #include // Diffusion properties #include #include #include #include #include #include #include "itksys/SystemTools.hxx" #include "itkImageFileReader.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "mitkCustomMimeType.h" #include "mitkDiffusionCoreIOMimeTypes.h" #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { DiffusionImageDicomReaderService:: DiffusionImageDicomReaderService(const DiffusionImageDicomReaderService & other) : AbstractFileReader(other) { } DiffusionImageDicomReaderService* DiffusionImageDicomReaderService::Clone() const { return new DiffusionImageDicomReaderService(*this); } DiffusionImageDicomReaderService:: ~DiffusionImageDicomReaderService() {} DiffusionImageDicomReaderService:: DiffusionImageDicomReaderService() : mitk::AbstractFileReader( CustomMimeType( mitk::DiffusionCoreIOMimeTypes::DWI_DICOM_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::DWI_DICOM_MIMETYPE_DESCRIPTION() ) { Options defaultOptions; + defaultOptions["Apply image rotation to gradients"] = true; defaultOptions["Load recursive"] = false; defaultOptions["Split mosaic"] = true; this->SetDefaultOptions(defaultOptions); m_ServiceReg = this->RegisterService(); } std::vector > DiffusionImageDicomReaderService::Read() { return InternalRead(); } std::vector > DiffusionImageDicomReaderService::InternalRead() { std::vector > result_images; OutputType::Pointer outputForCache = OutputType::New(); if ( this->GetInputLocation() == "") { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename to be read is empty!"); } else { const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, nullptr ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } try { Options options = this->GetOptions(); bool load_recursive = us::any_cast(options["Load recursive"]); bool split_mosaic = us::any_cast(options["Split mosaic"]); gdcm::Directory::FilenamesType complete_list; std::string folderName = itksys::SystemTools::GetFilenamePath( this->GetInputLocation() ); if( load_recursive ) { std::string subdir_prefix = ""; itksys::Directory rootdir; rootdir.Load( folderName.c_str() ); for( unsigned int idx=0; idxAddDistinguishingTag( mitk::DICOMTag(0x0028, 0x0010) ); // Number of Rows tagSorter->AddDistinguishingTag( mitk::DICOMTag(0x0028, 0x0011) ); // Number of Columns tagSorter->AddDistinguishingTag( mitk::DICOMTag(0x0028, 0x0030) ); // Pixel Spacing tagSorter->AddDistinguishingTag( mitk::DICOMTag(0x0018, 0x1164) ); // Imager Pixel Spacing tagSorter->AddDistinguishingTag( mitk::DICOMTag(0x0020, 0x0037) ); // Image Orientation (Patient) // TODO add tolerance parameter (l. 1572 of original code) // TODO handle as real vectors! cluster with configurable errors! tagSorter->AddDistinguishingTag( mitk::DICOMTag(0x0020, 0x000e) ); // Series Instance UID tagSorter->AddDistinguishingTag( mitk::DICOMTag(0x0018, 0x0050) ); // Slice Thickness tagSorter->AddDistinguishingTag( mitk::DICOMTag(0x0028, 0x0008) ); // Number of Frames //tagSorter->AddDistinguishingTag( mitk::DICOMTag(0x0020, 0x0052) ); // Frame of Reference UID auto tag_sop_instance_uid = mitk::DICOMSortByTag::New( mitk::DICOMTag(0x0008, 0x0018), nullptr ); // SOP instance UID (last resort, not really meaningful but decides clearly) auto tag_trigger_time = mitk::DICOMSortByTag::New( mitk::DICOMTag(0x0018, 0x1060), tag_sop_instance_uid.GetPointer() ); // trigger time auto tag_aqcuisition_time = mitk::DICOMSortByTag::New( mitk::DICOMTag(0x0008, 0x0032), tag_trigger_time.GetPointer()); // aqcuisition time auto tag_aqcuisition_number = mitk::DICOMSortByTag::New( mitk::DICOMTag(0x0020, 0x0012), tag_aqcuisition_time.GetPointer()); // aqcuisition number mitk::DICOMSortCriterion::ConstPointer sorting = mitk::SortByImagePositionPatient::New(tag_aqcuisition_number.GetPointer()).GetPointer(); tagSorter->SetSortCriterion( sorting ); // mosaic gdcmReader->SetResolveMosaic( split_mosaic ); gdcmReader->AddSortingElement( tagSorter ); gdcmReader->SetInputFiles( complete_list ); + gdcmReader->SetApplyRotationToGradients(us::any_cast(options["Apply image rotation to gradients"])); try { gdcmReader->AnalyzeInputFiles(); } catch( const itk::ExceptionObject &e) { MITK_ERROR << "Failed to analyze data. " << e.what(); } catch( const std::exception &se) { MITK_ERROR << "Std Exception " << se.what(); } gdcmReader->LoadImages(); for( unsigned int o = 0; o < gdcmReader->GetNumberOfOutputs(); o++ ) { mitk::Image::Pointer loaded_image = gdcmReader->GetOutput(o).GetMitkImage(); StringProperty::Pointer nameProp; if (gdcmReader->GetSeriesName(o)!="-") nameProp = StringProperty::New(gdcmReader->GetSeriesName(o)); else if (gdcmReader->GetStudyName(o)!="-") nameProp = StringProperty::New(gdcmReader->GetStudyName(o)); else nameProp = StringProperty::New(folderName); loaded_image->SetProperty("name", nameProp); std::string val = "-"; if (gdcmReader->patient_ids().size()>o) { val = gdcmReader->patient_ids().at(o); loaded_image->GetPropertyList()->SetStringProperty("DICOM.patient_id",val.c_str()); } if (gdcmReader->patient_names().size()>o) { val = gdcmReader->patient_names().at(o); loaded_image->GetPropertyList()->SetStringProperty("DICOM.patient_name",val.c_str()); } if (gdcmReader->study_instance_uids().size()>o) { val = gdcmReader->study_instance_uids().at(o); loaded_image->GetPropertyList()->SetStringProperty("DICOM.study_instance_uid",val.c_str()); } if (gdcmReader->series_instance_uids().size()>o) { val = gdcmReader->series_instance_uids().at(o); loaded_image->GetPropertyList()->SetStringProperty("DICOM.series_instance_uid",val.c_str()); } if (gdcmReader->sop_instance_uids().size()>o) { val = gdcmReader->sop_instance_uids().at(o); loaded_image->GetPropertyList()->SetStringProperty("DICOM.sop_instance_uid",val.c_str()); } if (gdcmReader->frame_of_reference_uids().size()>o) { val = gdcmReader->frame_of_reference_uids().at(o); loaded_image->GetPropertyList()->SetStringProperty("DICOM.frame_of_reference_uid",val.c_str()); } - result_images.push_back(loaded_image.GetPointer()); } // Since we have already read the tree, we can store it in a cache variable // so that it can be assigned to the DataObject in GenerateData(); m_OutputCache = outputForCache; m_CacheTime.Modified(); try { setlocale(LC_ALL, currLocale.c_str()); } catch(...) { MITK_INFO << "Could not reset locale " << currLocale; } } catch(std::exception& e) { try { setlocale(LC_ALL, currLocale.c_str()); } catch(...) { MITK_INFO << "Could not reset locale " << currLocale; } MITK_INFO << "Std::Exception while reading file!!"; MITK_INFO << e.what(); throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { try { setlocale(LC_ALL, currLocale.c_str()); } catch(...) { MITK_INFO << "Could not reset locale " << currLocale; } MITK_INFO << "Exception while reading file!!"; throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested vessel tree file!"); } } return result_images; } } //namespace MITK #endif diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiReaderService.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiReaderService.cpp index 22b96aa753..5f3a38ac34 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiReaderService.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNiftiReaderService.cpp @@ -1,347 +1,349 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkDiffusionImageNiftiReaderService_cpp #define __mitkDiffusionImageNiftiReaderService_cpp #include "mitkDiffusionImageNiftiReaderService.h" #include #include // Diffusion properties #include #include #include #include // ITK includes #include #include #include "itksys/SystemTools.hxx" #include "itkImageFileReader.h" #include "itkMetaDataObject.h" #include "itkNiftiImageIO.h" #include "mitkCustomMimeType.h" #include "mitkDiffusionCoreIOMimeTypes.h" #include #include #include #include #include "mitkIOUtil.h" #include namespace mitk { DiffusionImageNiftiReaderService:: DiffusionImageNiftiReaderService(const DiffusionImageNiftiReaderService & other) : AbstractFileReader(other) { } DiffusionImageNiftiReaderService* DiffusionImageNiftiReaderService::Clone() const { return new DiffusionImageNiftiReaderService(*this); } DiffusionImageNiftiReaderService:: ~DiffusionImageNiftiReaderService() {} DiffusionImageNiftiReaderService:: DiffusionImageNiftiReaderService(CustomMimeType mime_type, std::string mime_type_description ) : mitk::AbstractFileReader( mime_type, mime_type_description ) { + Options defaultOptions; + defaultOptions["Apply image rotation to gradients"] = true; + this->SetDefaultOptions(defaultOptions); + m_ServiceReg = this->RegisterService(); } //DiffusionImageNiftiReaderService:: //DiffusionImageNiftiReaderService() : mitk::AbstractFileReader( CustomMimeType( mitk::DiffusionCoreIOMimeTypes::DWI_NIFTI_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::DWI_NIFTI_MIMETYPE_DESCRIPTION() ) //{ // m_ServiceReg = this->RegisterService(); //} std::vector > DiffusionImageNiftiReaderService:: Read() { std::vector > result; // Since everything is completely read in GenerateOutputInformation() it is stored // in a cache variable. A timestamp is associated. // If the timestamp of the cache variable is newer than the MTime, we only need to // assign the cache variable to the DataObject. // Otherwise, the tree must be read again from the file and OuputInformation must // be updated! if(m_OutputCache.IsNull()) InternalRead(); result.push_back(m_OutputCache.GetPointer()); return result; } void DiffusionImageNiftiReaderService::InternalRead() { OutputType::Pointer outputForCache = OutputType::New(); if ( this->GetInputLocation() == "") { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename to be read is empty!"); } else { try { mitk::LocaleSwitch localeSwitch("C"); MITK_INFO << "DiffusionImageNiftiReaderService: reading image information"; VectorImageType::Pointer itkVectorImage; std::string ext = this->GetMimeType()->GetExtension( this->GetInputLocation() ); ext = itksys::SystemTools::LowerCase( ext ); if(ext == ".fsl" || ext == ".fslgz") { // create temporary file with correct ending for nifti-io std::string fname3 = "temp_dwi"; fname3 += ext == ".fsl" ? ".nii" : ".nii.gz"; itksys::SystemTools::CopyAFile(this->GetInputLocation().c_str(), fname3.c_str()); // create reader and read file typedef itk::Image ImageType4D; itk::NiftiImageIO::Pointer io2 = itk::NiftiImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(fname3); reader->SetImageIO(io2); reader->Update(); ImageType4D::Pointer img4 = reader->GetOutput(); // delete temporary file itksys::SystemTools::RemoveFile(fname3.c_str()); // convert 4D file to vector image itkVectorImage = VectorImageType::New(); VectorImageType::SpacingType spacing; ImageType4D::SpacingType spacing4 = img4->GetSpacing(); for(int i=0; i<3; i++) spacing[i] = spacing4[i]; itkVectorImage->SetSpacing( spacing ); // Set the image spacing VectorImageType::PointType origin; ImageType4D::PointType origin4 = img4->GetOrigin(); for(int i=0; i<3; i++) origin[i] = origin4[i]; itkVectorImage->SetOrigin( origin ); // Set the image origin VectorImageType::DirectionType direction; ImageType4D::DirectionType direction4 = img4->GetDirection(); for(int i=0; i<3; i++) for(int j=0; j<3; j++) direction[i][j] = direction4[i][j]; itkVectorImage->SetDirection( direction ); // Set the image direction VectorImageType::RegionType region; ImageType4D::RegionType region4 = img4->GetLargestPossibleRegion(); VectorImageType::RegionType::SizeType size; ImageType4D::RegionType::SizeType size4 = region4.GetSize(); for(int i=0; i<3; i++) size[i] = size4[i]; VectorImageType::RegionType::IndexType index; ImageType4D::RegionType::IndexType index4 = region4.GetIndex(); for(int i=0; i<3; i++) index[i] = index4[i]; region.SetSize(size); region.SetIndex(index); itkVectorImage->SetRegions( region ); itkVectorImage->SetVectorLength(size4[3]); itkVectorImage->Allocate(); itk::ImageRegionIterator it ( itkVectorImage, itkVectorImage->GetLargestPossibleRegion() ); typedef VectorImageType::PixelType VecPixType; for (it.GoToBegin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); VectorImageType::IndexType currentIndex = it.GetIndex(); for(int i=0; i<3; i++) index4[i] = currentIndex[i]; for(unsigned int ind=0; indGetPixel(index4); } it.Set(vec); } } else if(ext == ".nii" || ext == ".nii.gz") { // create reader and read file typedef itk::Image ImageType4D; itk::NiftiImageIO::Pointer io2 = itk::NiftiImageIO::New(); typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName( this->GetInputLocation() ); reader->SetImageIO(io2); reader->Update(); ImageType4D::Pointer img4 = reader->GetOutput(); // convert 4D file to vector image itkVectorImage = VectorImageType::New(); VectorImageType::SpacingType spacing; ImageType4D::SpacingType spacing4 = img4->GetSpacing(); for(int i=0; i<3; i++) spacing[i] = spacing4[i]; itkVectorImage->SetSpacing( spacing ); // Set the image spacing VectorImageType::PointType origin; ImageType4D::PointType origin4 = img4->GetOrigin(); for(int i=0; i<3; i++) origin[i] = origin4[i]; itkVectorImage->SetOrigin( origin ); // Set the image origin VectorImageType::DirectionType direction; ImageType4D::DirectionType direction4 = img4->GetDirection(); for(int i=0; i<3; i++) for(int j=0; j<3; j++) direction[i][j] = direction4[i][j]; itkVectorImage->SetDirection( direction ); // Set the image direction VectorImageType::RegionType region; ImageType4D::RegionType region4 = img4->GetLargestPossibleRegion(); VectorImageType::RegionType::SizeType size; ImageType4D::RegionType::SizeType size4 = region4.GetSize(); for(int i=0; i<3; i++) size[i] = size4[i]; VectorImageType::RegionType::IndexType index; ImageType4D::RegionType::IndexType index4 = region4.GetIndex(); for(int i=0; i<3; i++) index[i] = index4[i]; region.SetSize(size); region.SetIndex(index); itkVectorImage->SetRegions( region ); itkVectorImage->SetVectorLength(size4[3]); itkVectorImage->Allocate(); itk::ImageRegionIterator it ( itkVectorImage, itkVectorImage->GetLargestPossibleRegion() ); typedef VectorImageType::PixelType VecPixType; for (it.GoToBegin(); !it.IsAtEnd(); ++it) { VecPixType vec = it.Get(); VectorImageType::IndexType currentIndex = it.GetIndex(); for(int i=0; i<3; i++) index4[i] = currentIndex[i]; for(unsigned int ind=0; indGetPixel(index4); } it.Set(vec); } } // Diffusion Image information START GradientDirectionContainerType::Pointer DiffusionVectors = GradientDirectionContainerType::New(); MeasurementFrameType MeasurementFrame; double BValue = -1; // Diffusion Image information END if(ext == ".fsl" || ext == ".fslgz" || ext == ".nii" || ext == ".nii.gz") { std::string base_path = itksys::SystemTools::GetFilenamePath(this->GetInputLocation()); std::string base = this->GetMimeType()->GetFilenameWithoutExtension(this->GetInputLocation()); if (!base_path.empty()) { base = base_path + "/" + base; base_path += "/"; } // check for possible file names std::string bvals_file, bvecs_file; if (itksys::SystemTools::FileExists(base+".bvals")) bvals_file = base+".bvals"; else if (itksys::SystemTools::FileExists(base+".bval")) bvals_file = base+".bval"; else if (itksys::SystemTools::FileExists(base_path+"bvals")) bvals_file = base_path + "bvals"; else if (itksys::SystemTools::FileExists(base_path+"bval")) bvals_file = base_path + "bval"; if (itksys::SystemTools::FileExists(std::string(base+".bvecs").c_str())) bvecs_file = base+".bvecs"; else if (itksys::SystemTools::FileExists(base+".bvec")) bvals_file = base+".bvec"; else if (itksys::SystemTools::FileExists(base_path+"bvecs")) bvecs_file = base_path + "bvecs"; else if (itksys::SystemTools::FileExists(base_path+"bvec")) bvecs_file = base_path + "bvec"; DiffusionVectors = mitk::gradients::ReadBvalsBvecs(bvals_file, bvecs_file, BValue); - - for(int i=0; i<3; i++) - for(int j=0; j<3; j++) - MeasurementFrame[i][j] = i==j ? 1 : 0; + MeasurementFrame.set_identity(); } outputForCache = mitk::GrabItkImageMemory( itkVectorImage); // create BValueMap mitk::BValueMapProperty::BValueMap BValueMap = mitk::BValueMapProperty::CreateBValueMap(DiffusionVectors,BValue); mitk::DiffusionPropertyHelper::SetOriginalGradientContainer(outputForCache, DiffusionVectors); mitk::DiffusionPropertyHelper::SetMeasurementFrame(outputForCache, MeasurementFrame); mitk::DiffusionPropertyHelper::SetBValueMap(outputForCache, BValueMap); mitk::DiffusionPropertyHelper::SetReferenceBValue(outputForCache, BValue); + mitk::DiffusionPropertyHelper::SetApplyMatrixToGradients(outputForCache, us::any_cast(this->GetOptions()["Apply image rotation to gradients"])); mitk::DiffusionPropertyHelper::InitializeImage(outputForCache); // Since we have already read the tree, we can store it in a cache variable // so that it can be assigned to the DataObject in GenerateData(); m_OutputCache = outputForCache; m_CacheTime.Modified(); } catch(std::exception& e) { MITK_INFO << "Std::Exception while reading file!!"; MITK_INFO << e.what(); throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { MITK_INFO << "Exception while reading file!!"; throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested vessel tree file!"); } } } } //namespace MITK #endif diff --git a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdReaderService.cpp b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdReaderService.cpp index c0c364648a..455463e020 100644 --- a/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdReaderService.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/autoload/IO/mitkDiffusionImageNrrdReaderService.cpp @@ -1,245 +1,249 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkDiffusionImageNrrdReaderService_cpp #define __mitkDiffusionImageNrrdReaderService_cpp #include "mitkDiffusionImageNrrdReaderService.h" #include #include // Diffusion properties #include #include #include #include // ITK includes #include #include #include "itksys/SystemTools.hxx" #include "itkImageFileReader.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" #include "mitkCustomMimeType.h" #include "mitkDiffusionCoreIOMimeTypes.h" #include #include #include #include "mitkIOUtil.h" #include namespace mitk { DiffusionImageNrrdReaderService:: DiffusionImageNrrdReaderService(const DiffusionImageNrrdReaderService & other) : AbstractFileReader(other) { } DiffusionImageNrrdReaderService* DiffusionImageNrrdReaderService::Clone() const { return new DiffusionImageNrrdReaderService(*this); } DiffusionImageNrrdReaderService:: ~DiffusionImageNrrdReaderService() {} DiffusionImageNrrdReaderService:: DiffusionImageNrrdReaderService() : mitk::AbstractFileReader( CustomMimeType( mitk::DiffusionCoreIOMimeTypes::DWI_NRRD_MIMETYPE() ), mitk::DiffusionCoreIOMimeTypes::DWI_NRRD_MIMETYPE_DESCRIPTION() ) { + Options defaultOptions; + defaultOptions["Apply image rotation to gradients"] = true; + this->SetDefaultOptions(defaultOptions); m_ServiceReg = this->RegisterService(); } std::vector > DiffusionImageNrrdReaderService:: Read() { std::vector > result; // Since everything is completely read in GenerateOutputInformation() it is stored // in a cache variable. A timestamp is associated. // If the timestamp of the cache variable is newer than the MTime, we only need to // assign the cache variable to the DataObject. // Otherwise, the tree must be read again from the file and OuputInformation must // be updated! if(m_OutputCache.IsNull()) InternalRead(); result.push_back(m_OutputCache.GetPointer()); return result; } void DiffusionImageNrrdReaderService::InternalRead() { OutputType::Pointer outputForCache = OutputType::New(); if ( this->GetInputLocation() == "") { throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, the filename to be read is empty!"); } else { try { mitk::LocaleSwitch localeSwitch("C"); MITK_INFO << "DiffusionImageNrrdReaderService: reading image information"; VectorImageType::Pointer itkVectorImage; std::string ext = this->GetMimeType()->GetExtension( this->GetInputLocation() ); ext = itksys::SystemTools::LowerCase( ext ); if (ext == ".hdwi" || ext == ".dwi" || ext == ".nrrd") { typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(this->GetInputLocation()); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); reader->SetImageIO(io); reader->Update(); itkVectorImage = reader->GetOutput(); } // Diffusion Image information START GradientDirectionContainerType::Pointer DiffusionVectors = GradientDirectionContainerType::New(); GradientDirectionContainerType::Pointer OriginalDiffusionVectors = GradientDirectionContainerType::New(); MeasurementFrameType MeasurementFrame; float BValue = -1; // Diffusion Image information END if (ext == ".hdwi" || ext == ".dwi" || ext == ".nrrd") { itk::MetaDataDictionary imgMetaDictionary = itkVectorImage->GetMetaDataDictionary(); std::vector imgMetaKeys = imgMetaDictionary.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString; GradientDirectionType vect3d; int numberOfImages = 0; int numberOfGradientImages = 0; bool readb0 = false; double xx, xy, xz, yx, yy, yz, zx, zy, zz; for (; itKey != imgMetaKeys.end(); itKey ++) { double x,y,z; itk::ExposeMetaData (imgMetaDictionary, *itKey, metaString); if (itKey->find("DWMRI_gradient") != std::string::npos) { sscanf(metaString.c_str(), "%lf %lf %lf\n", &x, &y, &z); vect3d[0] = x; vect3d[1] = y; vect3d[2] = z; DiffusionVectors->InsertElement( numberOfImages, vect3d ); ++numberOfImages; // If the direction is 0.0, this is a reference image if (vect3d[0] == 0.0 && vect3d[1] == 0.0 && vect3d[2] == 0.0) { continue; } ++numberOfGradientImages;; } else if (itKey->find("DWMRI_b-value") != std::string::npos) { readb0 = true; BValue = atof(metaString.c_str()); } else if (itKey->find("measurement frame") != std::string::npos) { sscanf(metaString.c_str(), " ( %lf , %lf , %lf ) ( %lf , %lf , %lf ) ( %lf , %lf , %lf ) \n", &xx, &xy, &xz, &yx, &yy, &yz, &zx, &zy, &zz); if (xx>10e-10 || xy>10e-10 || xz>10e-10 || yx>10e-10 || yy>10e-10 || yz>10e-10 || zx>10e-10 || zy>10e-10 || zz>10e-10 ) { MeasurementFrame(0,0) = xx; MeasurementFrame(0,1) = xy; MeasurementFrame(0,2) = xz; MeasurementFrame(1,0) = yx; MeasurementFrame(1,1) = yy; MeasurementFrame(1,2) = yz; MeasurementFrame(2,0) = zx; MeasurementFrame(2,1) = zy; MeasurementFrame(2,2) = zz; } else { MeasurementFrame(0,0) = 1; MeasurementFrame(0,1) = 0; MeasurementFrame(0,2) = 0; MeasurementFrame(1,0) = 0; MeasurementFrame(1,1) = 1; MeasurementFrame(1,2) = 0; MeasurementFrame(2,0) = 0; MeasurementFrame(2,1) = 0; MeasurementFrame(2,2) = 1; } } } if(!readb0) { MITK_INFO << "BValue not specified in header file"; } } outputForCache = mitk::GrabItkImageMemory( itkVectorImage); // create BValueMap mitk::BValueMapProperty::BValueMap BValueMap = mitk::BValueMapProperty::CreateBValueMap(DiffusionVectors,BValue); mitk::DiffusionPropertyHelper::SetOriginalGradientContainer(outputForCache, DiffusionVectors); mitk::DiffusionPropertyHelper::SetMeasurementFrame(outputForCache, MeasurementFrame); mitk::DiffusionPropertyHelper::SetBValueMap(outputForCache, BValueMap); mitk::DiffusionPropertyHelper::SetReferenceBValue(outputForCache, BValue); + mitk::DiffusionPropertyHelper::SetApplyMatrixToGradients(outputForCache, us::any_cast(this->GetOptions()["Apply image rotation to gradients"])); mitk::DiffusionPropertyHelper::InitializeImage(outputForCache); // Since we have already read the tree, we can store it in a cache variable // so that it can be assigned to the DataObject in GenerateData(); m_OutputCache = outputForCache; m_CacheTime.Modified(); } catch(std::exception& e) { MITK_INFO << "Std::Exception while reading file!!"; MITK_INFO << e.what(); throw itk::ImageFileReaderException(__FILE__, __LINE__, e.what()); } catch(...) { MITK_INFO << "Exception while reading file!!"; throw itk::ImageFileReaderException(__FILE__, __LINE__, "Sorry, an error occurred while reading the requested vessel tree file!"); } } } } //namespace MITK #endif diff --git a/Modules/DiffusionImaging/DiffusionCore/include/DicomImport/mitkDiffusionDICOMFileReader.h b/Modules/DiffusionImaging/DiffusionCore/include/DicomImport/mitkDiffusionDICOMFileReader.h index 85166eb7a0..3a9a4ff708 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/DicomImport/mitkDiffusionDICOMFileReader.h +++ b/Modules/DiffusionImaging/DiffusionCore/include/DicomImport/mitkDiffusionDICOMFileReader.h @@ -1,86 +1,92 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKDIFFUSIONDICOMFILEREADER_H #define MITKDIFFUSIONDICOMFILEREADER_H #include "MitkDiffusionCoreExports.h" #include "mitkDICOMITKSeriesGDCMReader.h" #include "mitkDiffusionHeaderDICOMFileReader.h" #include "mitkClassicDICOMSeriesReader.h" namespace mitk { class MITKDIFFUSIONCORE_EXPORT DiffusionDICOMFileReader : public ClassicDICOMSeriesReader { public: mitkClassMacro( DiffusionDICOMFileReader, ClassicDICOMSeriesReader ) mitkCloneMacro( DiffusionDICOMFileReader ) itkNewMacro( DiffusionDICOMFileReader ) void AnalyzeInputFiles() override; bool LoadImages() override; bool CanHandleFile(const std::string &filename) override; void SetResolveMosaic( bool flag ) { m_ResolveMosaic = flag; } + void SetApplyRotationToGradients( bool apply ) + { + m_ApplyRotationToGradients = apply; + } + std::string GetStudyName(int i){ return m_Study_names.at(i); } std::string GetSeriesName(int i){ return m_Series_names.at(i); } std::vector sop_instance_uids() const; std::vector frame_of_reference_uids() const; std::vector series_instance_uids() const; std::vector study_instance_uids() const; std::vector patient_names() const; std::vector patient_ids() const; protected: DiffusionDICOMFileReader(); ~DiffusionDICOMFileReader() override; bool LoadSingleOutputImage( DiffusionHeaderDICOMFileReader::DICOMHeaderListType, DICOMImageBlockDescriptor&, bool); //mitk::DiffusionHeaderDICOMFileReader::DICOMHeaderListType m_RetrievedHeader; std::vector< mitk::DiffusionHeaderDICOMFileReader::DICOMHeaderListType > m_OutputHeaderContainer; std::vector< mitk::DiffusionHeaderDICOMFileReader::Pointer> m_OutputReaderContainer; std::vector< bool > m_IsMosaicData; std::vector< std::string > m_Study_names; std::vector< std::string > m_Series_names; bool m_ResolveMosaic; + bool m_ApplyRotationToGradients; std::vector< std::string > m_sop_instance_uids; std::vector< std::string > m_frame_of_reference_uids; std::vector< std::string > m_series_instance_uids; std::vector< std::string > m_study_instance_uids; std::vector< std::string > m_patient_names; std::vector< std::string > m_patient_ids; }; } #endif // MITKDIFFUSIONDICOMFILEREADER_H diff --git a/Modules/DiffusionImaging/DiffusionCore/include/IODataStructures/Properties/mitkDiffusionPropertyHelper.h b/Modules/DiffusionImaging/DiffusionCore/include/IODataStructures/Properties/mitkDiffusionPropertyHelper.h index e73ecfa823..a97b07f5a8 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/IODataStructures/Properties/mitkDiffusionPropertyHelper.h +++ b/Modules/DiffusionImaging/DiffusionCore/include/IODataStructures/Properties/mitkDiffusionPropertyHelper.h @@ -1,124 +1,126 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKDIFFUSIONPROPERTYHELPER_H #define MITKDIFFUSIONPROPERTYHELPER_H #include #include #include #include #include #include #include namespace mitk { /** \brief Helper class for mitk::Images containing diffusion weighted data * * This class takes a pointer to a mitk::Image containing diffusion weighted information and provides * functions to manipulate the diffusion meta-data. Will log an error if required information is * missing. */ class MITKDIFFUSIONCORE_EXPORT DiffusionPropertyHelper { public: typedef short DiffusionPixelType; typedef mitk::BValueMapProperty::BValueMap BValueMapType; typedef GradientDirectionsProperty::GradientDirectionType GradientDirectionType; typedef GradientDirectionsProperty::GradientDirectionsContainerType GradientDirectionsContainerType; typedef mitk::MeasurementFrameProperty::MeasurementFrameType MeasurementFrameType; typedef itk::VectorImage< DiffusionPixelType, 3> ImageType; static bool IsDiffusionWeightedImage(const mitk::Image *); static bool IsDiffusionWeightedImage(const mitk::DataNode* node); - static void ClearMeasurementFrameAndRotationMatrixFromGradients(mitk::Image* image); static void CopyProperties(mitk::Image* source, mitk::Image* target, bool ignore_original_gradients=false); static ImageType::Pointer GetItkVectorImage(Image *image); static const BValueMapType & GetBValueMap(const mitk::Image *); static float GetReferenceBValue(const mitk::Image *); static std::vector< int > GetBValueVector(const mitk::Image *); static const MeasurementFrameType & GetMeasurementFrame(const mitk::Image *); static GradientDirectionsContainerType::Pointer GetOriginalGradientContainer(const mitk::Image *); static GradientDirectionsContainerType::Pointer GetGradientContainer(const mitk::Image *); + static void SetApplyMatrixToGradients(mitk::Image* image, bool apply); + static void SetApplyMfToGradients(mitk::Image* image, bool apply); static void SetMeasurementFrame(mitk::Image* image, MeasurementFrameType mf); static void SetReferenceBValue(mitk::Image* image, float b_value); static void SetBValueMap(mitk::Image* image, BValueMapType map); static void SetOriginalGradientContainer(mitk::Image* image, GradientDirectionsContainerType::Pointer g_cont); static void SetGradientContainer(mitk::Image* image, GradientDirectionsContainerType::Pointer g_cont); static void RotateGradients(mitk::Image* image, vnl_matrix_fixed rotation_matrix, bool normalize_columns); static void RotateOriginalGradients(mitk::Image* image, vnl_matrix_fixed rotation_matrix, bool normalize_columns); static void AverageRedundantGradients(mitk::Image* image, double precision); static void InitializeImage(mitk::Image* image); static GradientDirectionsContainerType::Pointer CalcAveragedDirectionSet(double precision, GradientDirectionsContainerType::Pointer directions); static void SetupProperties(); // called in DiffusionCoreIOActivator static const std::string GetBvaluePropertyName() { return BVALUEMAPPROPERTYNAME; } static const std::string GetGradientContainerPropertyName() { return GRADIENTCONTAINERPROPERTYNAME; } static const std::string GetMeasurementFramePropertyName() { return MEASUREMENTFRAMEPROPERTYNAME; } protected: DiffusionPropertyHelper(); ~DiffusionPropertyHelper(); static const std::string GRADIENTCONTAINERPROPERTYNAME; static const std::string ORIGINALGRADIENTCONTAINERPROPERTYNAME; static const std::string MEASUREMENTFRAMEPROPERTYNAME; static const std::string REFERENCEBVALUEPROPERTYNAME; static const std::string BVALUEMAPPROPERTYNAME; static const std::string MODALITY; - static const std::string KEEP_ORIGINAL_DIRECTIONS; + static const std::string APPLY_MATRIX_TO_GRADIENTS; + static const std::string APPLY_MF_TO_GRADIENTS; /** * \brief Apply the previously set MeasurementFrame and the image rotation matrix to all gradients * * \warning first set the MeasurementFrame */ static void ApplyMeasurementFrameAndRotationMatrix(mitk::Image* image); /** * \brief Apply the inverse of the previously set MeasurementFrame and the image rotation matrix to all gradients * * \warning first set the MeasurementFrame */ static void UnApplyMeasurementFrameAndRotationMatrix(mitk::Image* image); /** * \brief Update the BValueMap (m_B_ValueMap) using the current gradient directions (m_Directions) * * \warning Have to be called after each manipulation on the GradientDirectionContainer * !especially after manipulation of the m_Directions (GetDirections()) container via pointer access! */ static void UpdateBValueMap(mitk::Image* image); /// Determines whether gradients can be considered to be equal static bool AreAlike(GradientDirectionType g1, GradientDirectionType g2, double precision); /// Get the b value belonging to an index static float GetB_Value(const mitk::Image* image, unsigned int i); }; } #endif diff --git a/Modules/DiffusionImaging/DiffusionCore/include/mitkDiffusionFunctionCollection.h b/Modules/DiffusionImaging/DiffusionCore/include/mitkDiffusionFunctionCollection.h index f103c2dee8..37935c9174 100644 --- a/Modules/DiffusionImaging/DiffusionCore/include/mitkDiffusionFunctionCollection.h +++ b/Modules/DiffusionImaging/DiffusionCore/include/mitkDiffusionFunctionCollection.h @@ -1,249 +1,259 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __mitkDiffusionFunctionCollection_h_ #define __mitkDiffusionFunctionCollection_h_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk{ class MITKDIFFUSIONCORE_EXPORT imv { public: static std::vector< std::pair< itk::Index<3>, double > > IntersectImage(const itk::Vector& spacing, itk::Index<3>& si, itk::Index<3>& ei, itk::ContinuousIndex& sf, itk::ContinuousIndex& ef); static itk::Point GetItkPoint(double point[3]) { itk::Point itkPoint; itkPoint[0] = static_cast(point[0]); itkPoint[1] = static_cast(point[1]); itkPoint[2] = static_cast(point[2]); return itkPoint; } template< class TType > static itk::Point GetItkPoint(double point[3]) { itk::Point itkPoint; itkPoint[0] = static_cast(point[0]); itkPoint[1] = static_cast(point[1]); itkPoint[2] = static_cast(point[2]); return itkPoint; } + template< class TType > + static itk::Vector GetItkVector(double point[3]) + { + itk::Vector itkVector; + itkVector[0] = static_cast(point[0]); + itkVector[1] = static_cast(point[1]); + itkVector[2] = static_cast(point[2]); + return itkVector; + } + template< class TPixelType, class TOutPixelType=TPixelType > static TOutPixelType GetImageValue(const itk::Point& itkP, bool interpolate, typename itk::LinearInterpolateImageFunction< itk::Image< TPixelType, 3 >, float >::Pointer interpolator) { if (interpolator==nullptr) return 0.0; itk::ContinuousIndex< float, 3> cIdx; interpolator->ConvertPointToContinuousIndex(itkP, cIdx); if (interpolator->IsInsideBuffer(cIdx)) { if (interpolate) return interpolator->EvaluateAtContinuousIndex(cIdx); else { itk::Index<3> idx; interpolator->ConvertContinuousIndexToNearestIndex(cIdx, idx); return interpolator->EvaluateAtIndex(idx); } } else return 0.0; } template< class TPixelType=unsigned char > static bool IsInsideMask(const itk::Point& itkP, bool interpolate, typename itk::LinearInterpolateImageFunction< itk::Image< TPixelType, 3 >, float >::Pointer interpolator, float threshold=0.5) { if (interpolator==nullptr) return false; itk::ContinuousIndex< float, 3> cIdx; interpolator->ConvertPointToContinuousIndex(itkP, cIdx); if (interpolator->IsInsideBuffer(cIdx)) { float value = 0.0; if (interpolate) value = interpolator->EvaluateAtContinuousIndex(cIdx); else { itk::Index<3> idx; interpolator->ConvertContinuousIndexToNearestIndex(cIdx, idx); value = interpolator->EvaluateAtIndex(idx); } if (value>=threshold) return true; } return false; } template< class TType=float > static vnl_matrix_fixed< TType, 3, 3 > GetRotationMatrixVnl(TType rx, TType ry, TType rz) { rx = rx*itk::Math::pi/180; ry = ry*itk::Math::pi/180; rz = rz*itk::Math::pi/180; vnl_matrix_fixed< TType, 3, 3 > rotX; rotX.set_identity(); rotX[1][1] = cos(rx); rotX[2][2] = rotX[1][1]; rotX[1][2] = -sin(rx); rotX[2][1] = -rotX[1][2]; vnl_matrix_fixed< TType, 3, 3 > rotY; rotY.set_identity(); rotY[0][0] = cos(ry); rotY[2][2] = rotY[0][0]; rotY[0][2] = sin(ry); rotY[2][0] = -rotY[0][2]; vnl_matrix_fixed< TType, 3, 3 > rotZ; rotZ.set_identity(); rotZ[0][0] = cos(rz); rotZ[1][1] = rotZ[0][0]; rotZ[0][1] = -sin(rz); rotZ[1][0] = -rotZ[0][1]; vnl_matrix_fixed< TType, 3, 3 > rot = rotZ*rotY*rotX; return rot; } template< class TType=float > static itk::Matrix< TType, 3, 3 > GetRotationMatrixItk(TType rx, TType ry, TType rz) { rx = rx*itk::Math::pi/180; ry = ry*itk::Math::pi/180; rz = rz*itk::Math::pi/180; itk::Matrix< TType, 3, 3 > rotX; rotX.SetIdentity(); rotX[1][1] = cos(rx); rotX[2][2] = rotX[1][1]; rotX[1][2] = -sin(rx); rotX[2][1] = -rotX[1][2]; itk::Matrix< TType, 3, 3 > rotY; rotY.SetIdentity(); rotY[0][0] = cos(ry); rotY[2][2] = rotY[0][0]; rotY[0][2] = sin(ry); rotY[2][0] = -rotY[0][2]; itk::Matrix< TType, 3, 3 > rotZ; rotZ.SetIdentity(); rotZ[0][0] = cos(rz); rotZ[1][1] = rotZ[0][0]; rotZ[0][1] = -sin(rz); rotZ[1][0] = -rotZ[0][1]; itk::Matrix< TType, 3, 3 > rot = rotZ*rotY*rotX; return rot; } }; class MITKDIFFUSIONCORE_EXPORT convert { public: static mitk::OdfImage::ItkOdfImageType::Pointer GetItkOdfFromTensorImage(mitk::Image::Pointer mitkImage); static mitk::OdfImage::Pointer GetOdfFromTensorImage(mitk::Image::Pointer mitkImage); static mitk::TensorImage::ItkTensorImageType::Pointer GetItkTensorFromTensorImage(mitk::Image::Pointer mitkImage); static mitk::PeakImage::ItkPeakImageType::Pointer GetItkPeakFromPeakImage(mitk::Image::Pointer mitkImage); template static typename itk::Image< itk::Vector, 3>::Pointer GetItkShFromShImage(mitk::Image::Pointer mitkImage) { mitk::ShImage::Pointer mitkShImage = dynamic_cast(mitkImage.GetPointer()); if (mitkShImage.IsNull()) mitkThrow() << "Input image is not a SH image!"; typename itk::Image< itk::Vector, 3>::Pointer itkvol; mitk::CastToItkImage(mitkImage, itkvol); return itkvol; } static mitk::OdfImage::ItkOdfImageType::Pointer GetItkOdfFromShImage(mitk::Image::Pointer mitkImage); static mitk::OdfImage::Pointer GetOdfFromShImage(mitk::Image::Pointer mitkImage); static mitk::OdfImage::ItkOdfImageType::Pointer GetItkOdfFromOdfImage(mitk::Image::Pointer mitkImage); }; class MITKDIFFUSIONCORE_EXPORT sh { public: static double factorial(int number); static void Cart2Sph(double x, double y, double z, double* spherical); static vnl_vector_fixed Sph2Cart(const double& theta, const double& phi, const double& rad); static double legendre0(int l); static double spherical_harmonic(int m,int l,double theta,double phi, bool complexPart); static double Yj(int m, int k, float theta, float phi, bool mrtrix=true); static vnl_matrix CalcShBasisForDirections(unsigned int sh_order, vnl_matrix U, bool mrtrix=true); static float GetValue(const vnl_vector& coefficients, const int& sh_order, const vnl_vector_fixed& dir, const bool mrtrix); static float GetValue(const vnl_vector &coefficients, const int &sh_order, const double theta, const double phi, const bool mrtrix); static unsigned int ShOrder(int num_coeffs); template static void SampleOdf(const vnl_vector& coefficients, itk::OrientationDistributionFunction& odf) { auto dirs = odf.GetDirections(); auto basis = CalcShBasisForDirections(ShOrder(coefficients.size()), *dirs); auto odf_vals = basis * coefficients; for (unsigned int i=0; i IndiciesVector; typedef mitk::BValueMapProperty::BValueMap BValueMap; typedef DiffusionPropertyHelper::GradientDirectionsContainerType GradientDirectionContainerType; typedef DiffusionPropertyHelper::GradientDirectionType GradientDirectionType; public: static GradientDirectionContainerType::Pointer ReadBvalsBvecs(std::string bvals_file, std::string bvecs_file, double& reference_bval); static void WriteBvalsBvecs(std::string bvals_file, std::string bvecs_file, GradientDirectionContainerType::Pointer gradients, double reference_bval); static std::vector GetAllUniqueDirections(const BValueMap &bValueMap, GradientDirectionContainerType *refGradientsContainer ); static bool CheckForDifferingShellDirections(const BValueMap &bValueMap, GradientDirectionContainerType::ConstPointer refGradientsContainer); static vnl_matrix ComputeSphericalHarmonicsBasis(const vnl_matrix & QBallReference, const unsigned int & LOrder); static vnl_matrix ComputeSphericalFromCartesian(const IndiciesVector & refShell, const GradientDirectionContainerType * refGradientsContainer); static GradientDirectionContainerType::Pointer CreateNormalizedUniqueGradientDirectionContainer(const BValueMap &bValueMap, const GradientDirectionContainerType * origninalGradentcontainer); }; } #endif //__mitkDiffusionFunctionCollection_h_ diff --git a/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkDiffusionDICOMFileReader.cpp b/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkDiffusionDICOMFileReader.cpp index 6e0571c822..c5bed76809 100644 --- a/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkDiffusionDICOMFileReader.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/src/DicomImport/mitkDiffusionDICOMFileReader.cpp @@ -1,435 +1,437 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDiffusionDICOMFileReader.h" #include "mitkDiffusionDICOMFileReaderHelper.h" #include "mitkDiffusionHeaderSiemensDICOMFileReader.h" #include "mitkDiffusionHeaderSiemensMosaicDICOMFileReader.h" #include "mitkDiffusionHeaderGEDICOMFileReader.h" #include "mitkDiffusionHeaderPhilipsDICOMFileReader.h" #include #include #include "mitkStringProperty.h" #include static void PerformHeaderAnalysis( mitk::DiffusionHeaderDICOMFileReader::DICOMHeaderListType headers ) { unsigned int images = headers.size(); unsigned int unweighted_images = 0; unsigned int weighted_images = 0; mitk::DiffusionHeaderDICOMFileReader::DICOMHeaderListType::const_iterator c_iter = headers.begin(); while( c_iter != headers.end() ) { const mitk::DiffusionImageDICOMHeaderInformation h = *c_iter; if( h.baseline ) unweighted_images++; if( h.b_value > 0 ) weighted_images++; ++c_iter; } MITK_INFO << " :: Analyzed volumes " << images << "\n" << " :: \t"<< unweighted_images << " b = 0" << "\n" << " :: \t"<< weighted_images << " b > 0"; } mitk::DiffusionDICOMFileReader::DiffusionDICOMFileReader() { - + m_ApplyRotationToGradients = true; + m_ResolveMosaic = true; } mitk::DiffusionDICOMFileReader::~DiffusionDICOMFileReader() { } bool mitk::DiffusionDICOMFileReader ::LoadImages() { unsigned int numberOfOutputs = this->GetNumberOfOutputs(); bool success = true; for(unsigned int o = 0; o < numberOfOutputs; ++o) { success &= this->LoadSingleOutputImage( this->m_OutputHeaderContainer.at(o), this->InternalGetOutput(o), this->m_IsMosaicData.at(o) ); } return success; } bool mitk::DiffusionDICOMFileReader ::LoadSingleOutputImage( DiffusionHeaderDICOMFileReader::DICOMHeaderListType retrievedHeader, DICOMImageBlockDescriptor& block, bool is_mosaic) { // prepare data reading DiffusionDICOMFileReaderHelper helper; DiffusionDICOMFileReaderHelper::VolumeFileNamesContainer filenames; const DICOMImageFrameList& frames = block.GetImageFrameList(); int numberOfDWImages = block.GetIntProperty("timesteps", 1); int numberOfFramesPerDWImage = frames.size() / numberOfDWImages; assert( int( double((double) frames.size() / (double) numberOfDWImages)) == numberOfFramesPerDWImage ); for( int idx = 0; idx < numberOfDWImages; idx++ ) { std::vector< std::string > FileNamesPerVolume; auto timeStepStart = frames.begin() + idx * numberOfFramesPerDWImage; auto timeStepEnd = frames.begin() + (idx+1) * numberOfFramesPerDWImage; for (auto frameIter = timeStepStart; frameIter != timeStepEnd; ++frameIter) { FileNamesPerVolume.push_back( (*frameIter)->Filename ); } filenames.push_back( FileNamesPerVolume ); } // TODO : only prototyping to test loading of diffusion images // we need some solution for the different types mitk::Image::Pointer output_image = mitk::Image::New(); mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer directions = mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::New(); double max_bvalue = 0; for( int idx = 0; idx < numberOfDWImages; idx++ ) { DiffusionImageDICOMHeaderInformation header = retrievedHeader.at(idx); if( max_bvalue < header.b_value ) max_bvalue = header.b_value; } // normalize the retrieved gradient directions according to the set b-value (maximal one) for( int idx = 0; idx < numberOfDWImages; idx++ ) { DiffusionImageDICOMHeaderInformation header = retrievedHeader.at(idx); mitk::DiffusionPropertyHelper::GradientDirectionType grad = header.g_vector; grad.normalize(); grad *= sqrt( header.b_value / max_bvalue ); directions->push_back( grad ); } // initialize the output image mitk::DiffusionPropertyHelper::SetOriginalGradientContainer(output_image, directions); mitk::DiffusionPropertyHelper::SetReferenceBValue(output_image, max_bvalue); if( is_mosaic && this->m_ResolveMosaic ) { mitk::DiffusionHeaderSiemensMosaicDICOMFileReader::Pointer mosaic_reader = mitk::DiffusionHeaderSiemensMosaicDICOMFileReader::New(); // retrieve the remaining meta-information needed for mosaic reconstruction // it suffices to get it exemplatory from the first file in the file list mosaic_reader->RetrieveMosaicInformation( filenames.at(0).at(0) ); mitk::MosaicDescriptor mdesc = mosaic_reader->GetMosaicDescriptor(); mitk::CastToMitkImage( helper.LoadMosaicToVector( filenames, mdesc ), output_image ); } else { mitk::CastToMitkImage( helper.LoadToVector( filenames ), output_image ); } + mitk::DiffusionPropertyHelper::SetApplyMatrixToGradients(output_image, m_ApplyRotationToGradients); mitk::DiffusionPropertyHelper::InitializeImage(output_image); output_image->SetProperty("diffusion.dicom.importname", mitk::StringProperty::New( helper.GetOutputName(filenames) ) ); block.SetMitkImage( (mitk::Image::Pointer) output_image ); return block.GetMitkImage().IsNotNull(); } std::vector mitk::DiffusionDICOMFileReader::patient_ids() const { return m_patient_ids; } std::vector mitk::DiffusionDICOMFileReader::patient_names() const { return m_patient_names; } std::vector mitk::DiffusionDICOMFileReader::study_instance_uids() const { return m_study_instance_uids; } std::vector mitk::DiffusionDICOMFileReader::series_instance_uids() const { return m_series_instance_uids; } std::vector mitk::DiffusionDICOMFileReader::frame_of_reference_uids() const { return m_frame_of_reference_uids; } std::vector mitk::DiffusionDICOMFileReader::sop_instance_uids() const { return m_sop_instance_uids; } void mitk::DiffusionDICOMFileReader ::AnalyzeInputFiles() { m_Study_names.clear(); m_Series_names.clear(); this->SetGroup3DandT(true); Superclass::AnalyzeInputFiles(); // collect output from superclass size_t number_of_outputs = this->GetNumberOfOutputs(); if(number_of_outputs == 0) { MITK_ERROR << "Failed to parse input, retrieved 0 outputs from SeriesGDCMReader "; } MITK_INFO("diffusion.dicomreader") << "Retrieved " << number_of_outputs << " outputs."; std::vector< bool > valid_input_list; for( unsigned int outputidx = 0; outputidx < this->GetNumberOfOutputs(); outputidx++ ) { DICOMImageBlockDescriptor block_0 = this->GetOutput(outputidx); // collect vendor ID from the first output, first image StringList inputFilename; DICOMImageFrameInfo::Pointer frame_0 = block_0.GetImageFrameList().at(0); inputFilename.push_back( frame_0->Filename ); mitk::DiffusionHeaderDICOMFileReader::Pointer headerReader; bool isMosaic = false; gdcm::Scanner gdcmScanner; gdcm::Tag t_sop_instance_uid(0x0008, 0x0018); gdcm::Tag t_frame_of_reference_uid(0x0020, 0x0052); gdcm::Tag t_series_instance_uid(0x0020, 0x000E); gdcm::Tag t_study_instance_uid(0x0020, 0x000D); gdcm::Tag t_patient_name(0x0010, 0x0010); gdcm::Tag t_patient_id(0x0010, 0x0020); gdcm::Tag t_vendor(0x008, 0x0070); gdcm::Tag t_imagetype(0x0008, 0x0008); gdcm::Tag t_StudyDescription(0x0008, 0x1030); gdcm::Tag t_SeriesDescription(0x0008, 0x103E); // add DICOM Tag for vendor gdcmScanner.AddTag( t_vendor ); // add DICOM Tag for image type gdcmScanner.AddTag( t_imagetype ); gdcmScanner.AddTag( t_StudyDescription ); gdcmScanner.AddTag( t_SeriesDescription ); gdcmScanner.AddTag( t_sop_instance_uid ); gdcmScanner.AddTag( t_frame_of_reference_uid ); gdcmScanner.AddTag( t_series_instance_uid ); gdcmScanner.AddTag( t_study_instance_uid ); gdcmScanner.AddTag( t_patient_name ); gdcmScanner.AddTag( t_patient_id ); if( gdcmScanner.Scan( inputFilename ) ) { // retrieve both vendor and image type const char* ch_vendor = gdcmScanner.GetValue( frame_0->Filename.c_str(), t_vendor ); const char* ch_image_type = gdcmScanner.GetValue( frame_0->Filename.c_str(), t_imagetype ); const char* temp = gdcmScanner.GetValue( frame_0->Filename.c_str(), t_sop_instance_uid ); if (temp!=nullptr) m_sop_instance_uids.push_back(std::string(temp)); else m_sop_instance_uids.push_back("-"); temp = nullptr; temp = gdcmScanner.GetValue( frame_0->Filename.c_str(), t_frame_of_reference_uid ); if (temp!=nullptr) m_frame_of_reference_uids.push_back(std::string(temp)); else m_frame_of_reference_uids.push_back("-"); temp = nullptr; temp = gdcmScanner.GetValue( frame_0->Filename.c_str(), t_series_instance_uid ); if (temp!=nullptr) m_series_instance_uids.push_back(std::string(temp)); else m_series_instance_uids.push_back("-"); temp = nullptr; temp = gdcmScanner.GetValue( frame_0->Filename.c_str(), t_study_instance_uid ); if (temp!=nullptr) m_study_instance_uids.push_back(std::string(temp)); else m_study_instance_uids.push_back("-"); temp = nullptr; temp = gdcmScanner.GetValue( frame_0->Filename.c_str(), t_patient_name ); if (temp!=nullptr) m_patient_names.push_back(std::string(temp)); else m_patient_names.push_back("-"); temp = nullptr; temp = gdcmScanner.GetValue( frame_0->Filename.c_str(), t_patient_id ); if (temp!=nullptr) m_patient_ids.push_back(std::string(temp)); else m_patient_ids.push_back("-"); if( ch_vendor == nullptr || ch_image_type == nullptr ) { MITK_WARN << "Unable to retrieve vendor/image information from " << frame_0->Filename.c_str() << "\n" << "Output " << outputidx+1 << " is not valid, skipping analysis."; valid_input_list.push_back(false); continue; } std::string vendor = std::string( ch_vendor ); std::string image_type = std::string( ch_image_type ); MITK_INFO("diffusion.dicomreader") << "Output " << outputidx+1 << " Got vendor: " << vendor << " image type " << image_type; // parse vendor tag if( vendor.find("SIEMENS") != std::string::npos || vendor.find("Siemens HealthCare GmbH") != std::string::npos ) { if( image_type.find("MOSAIC") != std::string::npos ) { headerReader = mitk::DiffusionHeaderSiemensMosaicDICOMFileReader::New(); isMosaic = true; } else { headerReader = mitk::DiffusionHeaderSiemensDICOMFileReader::New(); } } else if( vendor.find("GE") != std::string::npos ) { headerReader = mitk::DiffusionHeaderGEDICOMFileReader::New(); } else if( vendor.find("Philips") != std::string::npos ) { headerReader = mitk::DiffusionHeaderPhilipsDICOMFileReader::New(); } else { // unknown vendor } if( headerReader.IsNull() ) { MITK_ERROR << "No header reader for given vendor. "; valid_input_list.push_back(false); continue; } } else { valid_input_list.push_back(false); continue; } bool canread = false; // iterate over the threeD+t block int numberOfTimesteps = block_0.GetIntProperty("timesteps", 1); int framesPerTimestep = block_0.GetImageFrameList().size() / numberOfTimesteps; for( int idx = 0; idx < numberOfTimesteps; idx++ ) { int access_idx = idx * framesPerTimestep; DICOMImageFrameInfo::Pointer frame = this->GetOutput( outputidx ).GetImageFrameList().at( access_idx ); canread = headerReader->ReadDiffusionHeader( frame->Filename ); } if( canread ) { // collect the information mitk::DiffusionHeaderDICOMFileReader::DICOMHeaderListType retrievedHeader = headerReader->GetHeaderInformation(); m_IsMosaicData.push_back(isMosaic); m_OutputHeaderContainer.push_back( retrievedHeader ); m_OutputReaderContainer.push_back( headerReader ); valid_input_list.push_back(true); const char* ch_StudyDescription = gdcmScanner.GetValue( frame_0->Filename.c_str(), t_StudyDescription ); const char* ch_SeriesDescription = gdcmScanner.GetValue( frame_0->Filename.c_str(), t_SeriesDescription ); if( ch_StudyDescription != nullptr ) m_Study_names.push_back(ch_StudyDescription); else m_Study_names.push_back("-"); if( ch_SeriesDescription != nullptr ) m_Series_names.push_back(ch_SeriesDescription); else m_Series_names.push_back("-"); } } // check status of the outputs and remove the non-valid std::vector< DICOMImageBlockDescriptor > valid_outputs; for ( unsigned int outputidx = 0; outputidx < this->GetNumberOfOutputs(); ++outputidx ) { if( valid_input_list.at(outputidx) ) { valid_outputs.push_back( this->InternalGetOutput( outputidx ) ); } } // clear complete list this->ClearOutputs(); this->SetNumberOfOutputs( valid_outputs.size() ); // insert only the valid ones for ( unsigned int outputidx = 0; valid_outputs.size(); ++outputidx ) this->SetOutput( outputidx, valid_outputs.at( outputidx ) ); for( unsigned int outputidx = 0; outputidx < this->GetNumberOfOutputs(); outputidx++ ) { // TODO : Analyze outputs + header information, i.e. for the loading confidence MITK_INFO("diffusion.dicomreader") << "---- DICOM Analysis Report ---- :: Output " << outputidx+1 << " of " << this->GetNumberOfOutputs(); try{ PerformHeaderAnalysis( this->m_OutputHeaderContainer.at( outputidx) ); } catch( const std::exception& se) { MITK_ERROR << "STD Exception " << se.what(); } MITK_INFO("diffusion.dicomreader") << "==========================================="; } } bool mitk::DiffusionDICOMFileReader ::CanHandleFile(const std::string & /* filename */) { //FIXME : return true; } diff --git a/Modules/DiffusionImaging/DiffusionCore/src/IODataStructures/Properties/mitkDiffusionPropertyHelper.cpp b/Modules/DiffusionImaging/DiffusionCore/src/IODataStructures/Properties/mitkDiffusionPropertyHelper.cpp index 2a1e93df86..882f377814 100644 --- a/Modules/DiffusionImaging/DiffusionCore/src/IODataStructures/Properties/mitkDiffusionPropertyHelper.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/src/IODataStructures/Properties/mitkDiffusionPropertyHelper.cpp @@ -1,615 +1,615 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDiffusionPropertyHelper.h" #include #include #include #include #include #include #include #include #include const std::string mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME = "DWMRI.GradientDirections"; const std::string mitk::DiffusionPropertyHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME = "DWMRI.OriginalGradientDirections"; const std::string mitk::DiffusionPropertyHelper::MEASUREMENTFRAMEPROPERTYNAME = "DWMRI.MeasurementFrame"; const std::string mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME = "DWMRI.ReferenceBValue"; const std::string mitk::DiffusionPropertyHelper::BVALUEMAPPROPERTYNAME = "DWMRI.BValueMap"; const std::string mitk::DiffusionPropertyHelper::MODALITY = "DWMRI.Modality"; -const std::string mitk::DiffusionPropertyHelper::KEEP_ORIGINAL_DIRECTIONS = "DWMRI.KeepOriginalDirections"; +const std::string mitk::DiffusionPropertyHelper::APPLY_MATRIX_TO_GRADIENTS = "DWMRI.ApplyMatrixToGradients"; +const std::string mitk::DiffusionPropertyHelper::APPLY_MF_TO_GRADIENTS = "DWMRI.ApplyMfToGradients"; mitk::DiffusionPropertyHelper::DiffusionPropertyHelper() { } mitk::DiffusionPropertyHelper::~DiffusionPropertyHelper() { } mitk::DiffusionPropertyHelper::ImageType::Pointer mitk::DiffusionPropertyHelper::GetItkVectorImage(mitk::Image* image) { ImageType::Pointer vectorImage = ImageType::New(); mitk::CastToItkImage(image, vectorImage); return vectorImage; } mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer mitk::DiffusionPropertyHelper::CalcAveragedDirectionSet(double precision, GradientDirectionsContainerType::Pointer directions) { // save old and construct new direction container GradientDirectionsContainerType::Pointer newDirections = GradientDirectionsContainerType::New(); // fill new direction container for(GradientDirectionsContainerType::ConstIterator gdcitOld = directions->Begin(); gdcitOld != directions->End(); ++gdcitOld) { // already exists? bool found = false; for(GradientDirectionsContainerType::ConstIterator gdcitNew = newDirections->Begin(); gdcitNew != newDirections->End(); ++gdcitNew) { if(AreAlike(gdcitNew.Value(), gdcitOld.Value(), precision)) { found = true; break; } } // if not found, add it to new container if(!found) { newDirections->push_back(gdcitOld.Value()); } } return newDirections; } void mitk::DiffusionPropertyHelper::AverageRedundantGradients(mitk::Image* image, double precision) { GradientDirectionsContainerType::Pointer oldDirs = GetOriginalGradientContainer(image); GradientDirectionsContainerType::Pointer newDirs = CalcAveragedDirectionSet(precision, oldDirs); // if sizes equal, we do not need to do anything in this function if(oldDirs->size() == newDirs->size()) return; // new image ImageType::Pointer oldImage = ImageType::New(); mitk::CastToItkImage( image, oldImage); ImageType::Pointer newITKImage = ImageType::New(); newITKImage->SetSpacing( oldImage->GetSpacing() ); // Set the image spacing newITKImage->SetOrigin( oldImage->GetOrigin() ); // Set the image origin newITKImage->SetDirection( oldImage->GetDirection() ); // Set the image direction newITKImage->SetLargestPossibleRegion( oldImage->GetLargestPossibleRegion() ); newITKImage->SetVectorLength( newDirs->size() ); newITKImage->SetBufferedRegion( oldImage->GetLargestPossibleRegion() ); newITKImage->Allocate(); // average image data that corresponds to identical directions itk::ImageRegionIterator< ImageType > newIt(newITKImage, newITKImage->GetLargestPossibleRegion()); newIt.GoToBegin(); itk::ImageRegionIterator< ImageType > oldIt(oldImage, oldImage->GetLargestPossibleRegion()); oldIt.GoToBegin(); // initial new value of voxel ImageType::PixelType newVec; newVec.SetSize(newDirs->size()); newVec.AllocateElements(newDirs->size()); // find which gradients should be averaged GradientDirectionsContainerType::Pointer oldDirections = oldDirs; std::vector > dirIndices; for(GradientDirectionsContainerType::ConstIterator gdcitNew = newDirs->Begin(); gdcitNew != newDirs->End(); ++gdcitNew) { dirIndices.push_back(std::vector(0)); for(GradientDirectionsContainerType::ConstIterator gdcitOld = oldDirs->Begin(); gdcitOld != oldDirections->End(); ++gdcitOld) { if(AreAlike(gdcitNew.Value(), gdcitOld.Value(), precision)) { //MITK_INFO << gdcitNew.Value() << " " << gdcitOld.Value(); dirIndices[gdcitNew.Index()].push_back(gdcitOld.Index()); } } } //int ind1 = -1; while(!newIt.IsAtEnd()) { // init new vector with zeros newVec.Fill(0.0); // the old voxel value with duplicates ImageType::PixelType oldVec = oldIt.Get(); for(unsigned int i=0; iGetProperty(mitk::DiffusionPropertyHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str()).IsNull() ) { return; } GradientDirectionsContainerType::Pointer originalDirections = GetOriginalGradientContainer(image); MeasurementFrameType measurementFrame = GetMeasurementFrame(image); mitk::Vector3D s = image->GetGeometry()->GetSpacing(); mitk::VnlVector c0 = image->GetGeometry()->GetMatrixColumn(0)/s[0]; mitk::VnlVector c1 = image->GetGeometry()->GetMatrixColumn(1)/s[1]; mitk::VnlVector c2 = image->GetGeometry()->GetMatrixColumn(2)/s[2]; MeasurementFrameType imageRotationMatrix; imageRotationMatrix[0][0] = c0[0]; imageRotationMatrix[1][0] = c0[1]; imageRotationMatrix[2][0] = c0[2]; imageRotationMatrix[0][1] = c1[0]; imageRotationMatrix[1][1] = c1[1]; imageRotationMatrix[2][1] = c1[2]; imageRotationMatrix[0][2] = c2[0]; imageRotationMatrix[1][2] = c2[1]; imageRotationMatrix[2][2] = c2[2]; GradientDirectionsContainerType::Pointer directions = GradientDirectionsContainerType::New(); if( originalDirections.IsNull() || ( originalDirections->size() == 0 ) ) { // original direction container was not set return; } - bool keep_originals = false; - image->GetPropertyList()->GetBoolProperty(mitk::DiffusionPropertyHelper::KEEP_ORIGINAL_DIRECTIONS.c_str(), keep_originals); + bool apply_matrix = true; + image->GetPropertyList()->GetBoolProperty(mitk::DiffusionPropertyHelper::APPLY_MATRIX_TO_GRADIENTS.c_str(), apply_matrix); + bool apply_mf = true; + image->GetPropertyList()->GetBoolProperty(mitk::DiffusionPropertyHelper::APPLY_MF_TO_GRADIENTS.c_str(), apply_mf); - if (!keep_originals) + if (apply_matrix) { - MITK_INFO << "Applying measurement frame to diffusion-gradient directions:"; - std::cout << measurementFrame << std::endl; MITK_INFO << "Applying image rotation to diffusion-gradient directions:"; std::cout << imageRotationMatrix << std::endl; } + if (apply_mf) + { + MITK_INFO << "Applying measurement frame to diffusion-gradient directions:"; + std::cout << measurementFrame << std::endl; + } int c = 0; for(GradientDirectionsContainerType::ConstIterator gdcit = originalDirections->Begin(); gdcit != originalDirections->End(); ++gdcit) { vnl_vector vec = gdcit.Value(); - if (!keep_originals) - { + if (apply_matrix) vec = vec.pre_multiply(measurementFrame); + if (apply_mf) vec = vec.pre_multiply(imageRotationMatrix); - } directions->InsertElement(c, vec); c++; } SetGradientContainer(image, directions); } void mitk::DiffusionPropertyHelper::UnApplyMeasurementFrameAndRotationMatrix(mitk::Image* image) { if( image->GetProperty(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).IsNull() ) { return; } GradientDirectionsContainerType::Pointer modifiedDirections = GetGradientContainer(image); MeasurementFrameType measurementFrame = GetMeasurementFrame(image); measurementFrame = vnl_matrix_inverse(measurementFrame).pinverse(); mitk::Vector3D s = image->GetGeometry()->GetSpacing(); mitk::VnlVector c0 = image->GetGeometry()->GetMatrixColumn(0)/s[0]; mitk::VnlVector c1 = image->GetGeometry()->GetMatrixColumn(1)/s[1]; mitk::VnlVector c2 = image->GetGeometry()->GetMatrixColumn(2)/s[2]; MeasurementFrameType imageRotationMatrix; imageRotationMatrix[0][0] = c0[0]; imageRotationMatrix[1][0] = c0[1]; imageRotationMatrix[2][0] = c0[2]; imageRotationMatrix[0][1] = c1[0]; imageRotationMatrix[1][1] = c1[1]; imageRotationMatrix[2][1] = c1[2]; imageRotationMatrix[0][2] = c2[0]; imageRotationMatrix[1][2] = c2[1]; imageRotationMatrix[2][2] = c2[2]; imageRotationMatrix = vnl_matrix_inverse(imageRotationMatrix).pinverse(); GradientDirectionsContainerType::Pointer directions = GradientDirectionsContainerType::New(); if( modifiedDirections.IsNull() || ( modifiedDirections->size() == 0 ) ) { // original direction container was not set return; } - bool keep_originals = false; - image->GetPropertyList()->GetBoolProperty(mitk::DiffusionPropertyHelper::KEEP_ORIGINAL_DIRECTIONS.c_str(), keep_originals); + bool apply_matrix = true; + image->GetPropertyList()->GetBoolProperty(mitk::DiffusionPropertyHelper::APPLY_MATRIX_TO_GRADIENTS.c_str(), apply_matrix); + bool apply_mf = true; + image->GetPropertyList()->GetBoolProperty(mitk::DiffusionPropertyHelper::APPLY_MF_TO_GRADIENTS.c_str(), apply_mf); - if (!keep_originals) + if (apply_matrix) { MITK_INFO << "Reverting image rotation to diffusion-gradient directions:"; std::cout << imageRotationMatrix << std::endl; + } + if (apply_mf) + { MITK_INFO << "Reverting measurement frame to diffusion-gradient directions:"; std::cout << measurementFrame << std::endl; } int c = 0; for(GradientDirectionsContainerType::ConstIterator gdcit = modifiedDirections->Begin(); gdcit != modifiedDirections->End(); ++gdcit) { vnl_vector vec = gdcit.Value(); - if (!keep_originals) - { + if (apply_matrix) vec = vec.pre_multiply(imageRotationMatrix); + if (apply_mf) vec = vec.pre_multiply(measurementFrame); - } directions->InsertElement(c, vec); c++; } SetOriginalGradientContainer(image, directions); } -void mitk::DiffusionPropertyHelper::ClearMeasurementFrameAndRotationMatrixFromGradients(mitk::Image* image) +void mitk::DiffusionPropertyHelper::SetApplyMatrixToGradients(mitk::Image* image, bool apply) { - GradientDirectionsContainerType::Pointer originalDirections = GetOriginalGradientContainer(image); - GradientDirectionsContainerType::Pointer directions = GradientDirectionsContainerType::New(); - - if(originalDirections.IsNull() || originalDirections->size()==0) - return; - - int c = 0; - for(GradientDirectionsContainerType::ConstIterator gdcit = originalDirections->Begin(); - gdcit != originalDirections->End(); ++gdcit) - { - vnl_vector vec = gdcit.Value(); - directions->InsertElement(c, vec); - c++; - } - - image->GetPropertyList()->ReplaceProperty( mitk::DiffusionPropertyHelper::KEEP_ORIGINAL_DIRECTIONS.c_str(), mitk::BoolProperty::New(true) ); - SetGradientContainer(image, directions); + image->GetPropertyList()->SetProperty( mitk::DiffusionPropertyHelper::APPLY_MATRIX_TO_GRADIENTS.c_str(), mitk::BoolProperty::New(apply) ); } +void mitk::DiffusionPropertyHelper::SetApplyMfToGradients(mitk::Image* image, bool apply) +{ + image->GetPropertyList()->SetProperty( mitk::DiffusionPropertyHelper::APPLY_MF_TO_GRADIENTS.c_str(), mitk::BoolProperty::New(apply) ); +} void mitk::DiffusionPropertyHelper::UpdateBValueMap(mitk::Image* image) { BValueMapType b_ValueMap; if(image->GetProperty(mitk::DiffusionPropertyHelper::BVALUEMAPPROPERTYNAME.c_str()).IsNotNull()) b_ValueMap = GetBValueMap(image); if(!b_ValueMap.empty()) b_ValueMap.clear(); if(GetGradientContainer(image).IsNotNull()) { GradientDirectionsContainerType::Pointer directions = GetGradientContainer(image); for(auto gdcit = directions->Begin(); gdcit!=directions->End(); ++gdcit) { b_ValueMap[GetB_Value(image, gdcit.Index())].push_back(gdcit.Index()); } } SetBValueMap(image, b_ValueMap); } bool mitk::DiffusionPropertyHelper::AreAlike(GradientDirectionType g1, GradientDirectionType g2, double precision) { GradientDirectionType diff = g1 - g2; GradientDirectionType diff2 = g1 + g2; return diff.two_norm() < precision || diff2.two_norm() < precision; } float mitk::DiffusionPropertyHelper::GetB_Value(const mitk::Image* image, unsigned int i) { GradientDirectionsContainerType::Pointer directions = GetGradientContainer(image); float b_value = GetReferenceBValue(image); if(i > directions->Size()-1) return -1; if(directions->ElementAt(i).one_norm() <= 0.0) { return 0; } else { double twonorm = directions->ElementAt(i).two_norm(); double bval = b_value*twonorm*twonorm; if (bval<0) bval = ceil(bval - 0.5); else bval = floor(bval + 0.5); return bval; } } void mitk::DiffusionPropertyHelper::CopyProperties(mitk::Image* source, mitk::Image* target, bool ignore_original_gradients) { mitk::PropertyList::Pointer props = source->GetPropertyList()->Clone(); if (ignore_original_gradients) props->RemoveProperty(mitk::DiffusionPropertyHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME); target->SetPropertyList(props); } void mitk::DiffusionPropertyHelper::InitializeImage(mitk::Image* image) { - if ( image->GetProperty(mitk::DiffusionPropertyHelper::KEEP_ORIGINAL_DIRECTIONS.c_str()).IsNull() ) - image->SetProperty( mitk::DiffusionPropertyHelper::KEEP_ORIGINAL_DIRECTIONS.c_str(), mitk::BoolProperty::New(false) ); + if ( image->GetProperty(mitk::DiffusionPropertyHelper::APPLY_MATRIX_TO_GRADIENTS.c_str()).IsNull() ) + image->SetProperty( mitk::DiffusionPropertyHelper::APPLY_MATRIX_TO_GRADIENTS.c_str(), mitk::BoolProperty::New(true) ); + + if ( image->GetProperty(mitk::DiffusionPropertyHelper::APPLY_MF_TO_GRADIENTS.c_str()).IsNull() ) + image->SetProperty( mitk::DiffusionPropertyHelper::APPLY_MF_TO_GRADIENTS.c_str(), mitk::BoolProperty::New(true) ); if( image->GetProperty(mitk::DiffusionPropertyHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str()).IsNull() ) { - // we don't have the original gradient directions. Therefore use the modified directions and roatate them back. + // we don't have the original gradient directions. Therefore use the modified directions and rotate them back. UnApplyMeasurementFrameAndRotationMatrix(image); } else ApplyMeasurementFrameAndRotationMatrix(image); UpdateBValueMap(image); // initialize missing properties mitk::MeasurementFrameProperty::Pointer mf = dynamic_cast( image->GetProperty(MEASUREMENTFRAMEPROPERTYNAME.c_str()).GetPointer()); if( mf.IsNull() ) { //no measurement frame present, identity is assumed MeasurementFrameType identity; identity.set_identity(); image->GetPropertyList()->ReplaceProperty( mitk::DiffusionPropertyHelper::MEASUREMENTFRAMEPROPERTYNAME.c_str(), mitk::MeasurementFrameProperty::New( identity )); } } bool mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(const mitk::DataNode* node) { if ( node==nullptr ) return false; if ( node->GetData()==nullptr ) return false; return IsDiffusionWeightedImage(dynamic_cast(node->GetData())); } bool mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(const mitk::Image * image) { bool isDiffusionWeightedImage( true ); if( image == nullptr ) { isDiffusionWeightedImage = false; } if( isDiffusionWeightedImage ) { mitk::FloatProperty::Pointer referenceBValue = dynamic_cast(image->GetProperty(REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer()); if( referenceBValue.IsNull() ) { isDiffusionWeightedImage = false; } } unsigned int gradientDirections( 0 ); if( isDiffusionWeightedImage ) { mitk::GradientDirectionsProperty::Pointer gradientDirectionsProperty = dynamic_cast(image->GetProperty(GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer()); if( gradientDirectionsProperty.IsNull() ) { isDiffusionWeightedImage = false; } else { gradientDirections = gradientDirectionsProperty->GetGradientDirectionsContainer()->size(); } } if( isDiffusionWeightedImage ) { unsigned int components = image->GetPixelType().GetNumberOfComponents(); if( components != gradientDirections ) { isDiffusionWeightedImage = false; } } return isDiffusionWeightedImage; } const mitk::DiffusionPropertyHelper::BValueMapType & mitk::DiffusionPropertyHelper::GetBValueMap(const mitk::Image *image) { return dynamic_cast(image->GetProperty(BVALUEMAPPROPERTYNAME.c_str()).GetPointer())->GetBValueMap(); } std::vector< int > mitk::DiffusionPropertyHelper::GetBValueVector(const mitk::Image* image) { auto gcon = mitk::DiffusionPropertyHelper::GetGradientContainer(image); float b_value = mitk::DiffusionPropertyHelper::GetReferenceBValue(image); std::vector< int > bvalues; for (unsigned int i=0; iSize(); ++i) { double twonorm = gcon->ElementAt(i).two_norm(); double bval = b_value*twonorm*twonorm; if (bval<0) bval = ceil(bval - 0.5); else bval = floor(bval + 0.5); bvalues.push_back(bval); } return bvalues; } float mitk::DiffusionPropertyHelper::GetReferenceBValue(const mitk::Image *image) { return dynamic_cast(image->GetProperty(REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer())->GetValue(); } const mitk::DiffusionPropertyHelper::MeasurementFrameType & mitk::DiffusionPropertyHelper::GetMeasurementFrame(const mitk::Image *image) { mitk::MeasurementFrameProperty::Pointer mf = dynamic_cast( image->GetProperty(MEASUREMENTFRAMEPROPERTYNAME.c_str()).GetPointer()); if( mf.IsNull() ) { //no measurement frame present, identity is assumed MeasurementFrameType identity; identity.set_identity(); mf = mitk::MeasurementFrameProperty::New( identity ); } return mf->GetMeasurementFrame(); } mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer mitk::DiffusionPropertyHelper::GetOriginalGradientContainer(const mitk::Image *image) { return dynamic_cast(image->GetProperty(ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer())->GetGradientDirectionsContainer(); } mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer mitk::DiffusionPropertyHelper::GetGradientContainer(const mitk::Image *image) { return dynamic_cast(image->GetProperty(GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer())->GetGradientDirectionsContainer(); } void mitk::DiffusionPropertyHelper::SetReferenceBValue(mitk::Image* image, float b_value) { image->GetPropertyList()->ReplaceProperty(REFERENCEBVALUEPROPERTYNAME.c_str(), mitk::FloatProperty::New(b_value)); } void mitk::DiffusionPropertyHelper::SetBValueMap(mitk::Image* image, BValueMapType map) { image->GetPropertyList()->ReplaceProperty( mitk::DiffusionPropertyHelper::BVALUEMAPPROPERTYNAME.c_str(), mitk::BValueMapProperty::New(map)); } void mitk::DiffusionPropertyHelper::SetOriginalGradientContainer(mitk::Image* image, GradientDirectionsContainerType::Pointer g_cont) { image->GetPropertyList()->ReplaceProperty(ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str(), mitk::GradientDirectionsProperty::New(g_cont)); } void mitk::DiffusionPropertyHelper::SetGradientContainer(mitk::Image* image, GradientDirectionsContainerType::Pointer g_cont) { image->GetPropertyList()->ReplaceProperty(GRADIENTCONTAINERPROPERTYNAME.c_str(), mitk::GradientDirectionsProperty::New(g_cont)); } void mitk::DiffusionPropertyHelper::SetMeasurementFrame(mitk::Image* image, MeasurementFrameType mf) { image->GetPropertyList()->ReplaceProperty( MEASUREMENTFRAMEPROPERTYNAME.c_str(), mitk::MeasurementFrameProperty::New( mf ) ); } void mitk::DiffusionPropertyHelper::RotateGradients(mitk::Image* image, vnl_matrix_fixed rotation_matrix, bool normalize_columns) { if (normalize_columns) rotation_matrix = rotation_matrix.normalize_columns(); int c = 0; auto new_gradients = GradientDirectionsContainerType::New(); auto old_gradients = GetGradientContainer(image); for(auto gdcit = old_gradients->Begin(); gdcit != old_gradients->End(); ++gdcit) { vnl_vector vec = gdcit.Value(); vec = vec.pre_multiply(rotation_matrix); new_gradients->InsertElement(c, vec); c++; } SetGradientContainer(image, new_gradients); } void mitk::DiffusionPropertyHelper::RotateOriginalGradients(mitk::Image* image, vnl_matrix_fixed rotation_matrix, bool normalize_columns) { if (normalize_columns) rotation_matrix = rotation_matrix.normalize_columns(); int c = 0; auto new_gradients = GradientDirectionsContainerType::New(); auto old_gradients = GetOriginalGradientContainer(image); for(auto gdcit = old_gradients->Begin(); gdcit != old_gradients->End(); ++gdcit) { vnl_vector vec = gdcit.Value(); vec = vec.pre_multiply(rotation_matrix); new_gradients->InsertElement(c, vec); c++; } SetOriginalGradientContainer(image, new_gradients); } void mitk::DiffusionPropertyHelper::SetupProperties() { //register relevant properties //non-persistent properties mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::BVALUEMAPPROPERTYNAME, "This map stores which b values belong to which gradients."); mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME, "The original gradients used during acquisition. This property may be empty."); //persistent properties mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME, "The reference b value the gradients are normalized to."); mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::MEASUREMENTFRAMEPROPERTYNAME, "The measurment frame used during acquisition."); mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME, "The gradients after applying measurement frame and image matrix."); mitk::CoreServices::GetPropertyDescriptions()->AddDescription(mitk::DiffusionPropertyHelper::MODALITY, "Defines the modality used for acquisition. DWMRI signifies diffusion weighted images."); mitk::PropertyPersistenceInfo::Pointer PPI_referenceBValue = mitk::PropertyPersistenceInfo::New(); PPI_referenceBValue->SetNameAndKey(mitk::DiffusionPropertyHelper::REFERENCEBVALUEPROPERTYNAME, "DWMRI_b-value"); mitk::PropertyPersistenceInfo::Pointer PPI_measurementFrame = mitk::PropertyPersistenceInfo::New(); PPI_measurementFrame->SetNameAndKey(mitk::DiffusionPropertyHelper::MEASUREMENTFRAMEPROPERTYNAME, "measurement frame"); mitk::PropertyPersistenceInfo::Pointer PPI_gradientContainer = mitk::PropertyPersistenceInfo::New(); PPI_gradientContainer->SetNameAndKey(mitk::DiffusionPropertyHelper::GRADIENTCONTAINERPROPERTYNAME, "DWMRI_gradient"); mitk::PropertyPersistenceInfo::Pointer PPI_modality = mitk::PropertyPersistenceInfo::New(); PPI_modality->SetNameAndKey(mitk::DiffusionPropertyHelper::MODALITY, "modality"); mitk::CoreServices::GetPropertyPersistence()->AddInfo(PPI_referenceBValue.GetPointer() , true); mitk::CoreServices::GetPropertyPersistence()->AddInfo(PPI_measurementFrame.GetPointer(), true); mitk::CoreServices::GetPropertyPersistence()->AddInfo(PPI_gradientContainer.GetPointer(), true); mitk::CoreServices::GetPropertyPersistence()->AddInfo(PPI_modality.GetPointer(), true); } diff --git a/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp b/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp index 254fe1a463..eaa009d443 100644 --- a/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp +++ b/Modules/DiffusionImaging/DiffusionCore/src/mitkDiffusionFunctionCollection.cpp @@ -1,704 +1,707 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkDiffusionFunctionCollection.h" #include "mitkNumericTypes.h" #include #include #include #include #include "itkVectorContainer.h" #include "vnl/vnl_vector.h" #include #include #include #include #include #include // Intersect a finite line (with end points p0 and p1) with all of the // cells of a vtkImageData std::vector< std::pair< itk::Index<3>, double > > mitk::imv::IntersectImage(const itk::Vector& spacing, itk::Index<3>& si, itk::Index<3>& ei, itk::ContinuousIndex& sf, itk::ContinuousIndex& ef) { std::vector< std::pair< itk::Index<3>, double > > out; if (si == ei) { double d[3]; for (int i=0; i<3; ++i) d[i] = static_cast(sf[i]-ef[i])*spacing[i]; double len = std::sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] ); out.push_back( std::pair< itk::Index<3>, double >(si, len) ); return out; } double bounds[6]; double entrancePoint[3]; double exitPoint[3]; double startPoint[3]; double endPoint[3]; double t0, t1; for (unsigned int i=0; i<3; ++i) { startPoint[i] = static_cast(sf[i]); endPoint[i] = static_cast(ef[i]); if (si[i]>ei[i]) { auto t = si[i]; si[i] = ei[i]; ei[i] = t; } } for (auto x = si[0]; x<=ei[0]; ++x) for (auto y = si[1]; y<=ei[1]; ++y) for (auto z = si[2]; z<=ei[2]; ++z) { bounds[0] = static_cast(x) - 0.5; bounds[1] = static_cast(x) + 0.5; bounds[2] = static_cast(y) - 0.5; bounds[3] = static_cast(y) + 0.5; bounds[4] = static_cast(z) - 0.5; bounds[5] = static_cast(z) + 0.5; int entryPlane; int exitPlane; int hit = vtkBox::IntersectWithLine(bounds, startPoint, endPoint, t0, t1, entrancePoint, exitPoint, entryPlane, exitPlane); if (hit) { if (entryPlane>=0 && exitPlane>=0) { double d[3]; for (int i=0; i<3; ++i) d[i] = (exitPoint[i] - entrancePoint[i])*spacing[i]; double len = std::sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] ); itk::Index<3> idx; idx[0] = x; idx[1] = y; idx[2] = z; out.push_back( std::pair< itk::Index<3>, double >(idx, len) ); } else if (entryPlane>=0) { double d[3]; for (int i=0; i<3; ++i) d[i] = (static_cast(ef[i]) - entrancePoint[i])*spacing[i]; double len = std::sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] ); itk::Index<3> idx; idx[0] = x; idx[1] = y; idx[2] = z; out.push_back( std::pair< itk::Index<3>, double >(idx, len) ); } else if (exitPlane>=0) { double d[3]; for (int i=0; i<3; ++i) d[i] = (exitPoint[i]-static_cast(sf[i]))*spacing[i]; double len = std::sqrt( d[0]*d[0] + d[1]*d[1] + d[2]*d[2] ); itk::Index<3> idx; idx[0] = x; idx[1] = y; idx[2] = z; out.push_back( std::pair< itk::Index<3>, double >(idx, len) ); } } } return out; } //------------------------- SH-function ------------------------------------ double mitk::sh::factorial(int number) { if(number <= 1) return 1; double result = 1.0; for(int i=1; i<=number; i++) result *= i; return result; } void mitk::sh::Cart2Sph(double x, double y, double z, double *spherical) { double phi, th, rad; rad = sqrt(x*x+y*y+z*z); if( rad < mitk::eps ) { th = itk::Math::pi/2; phi = itk::Math::pi/2; } else { th = acos(z/rad); phi = atan2(y, x); } spherical[0] = phi; spherical[1] = th; spherical[2] = rad; } vnl_vector_fixed mitk::sh::Sph2Cart(const double& theta, const double& phi, const double& rad) { vnl_vector_fixed dir; dir[0] = rad * sin(theta) * cos(phi); dir[1] = rad * sin(theta) * sin(phi); dir[2] = rad * cos(theta); return dir; } double mitk::sh::legendre0(int l) { if( l%2 != 0 ) { return 0; } else { double prod1 = 1.0; for(int i=1;i(::boost::math::spherical_harmonic_r(static_cast(k), -m, theta, phi)); else if (m==0) return static_cast(::boost::math::spherical_harmonic_r(static_cast(k), m, theta, phi)); else return pow(-1.0,m)*sqrt(2.0)*static_cast(::boost::math::spherical_harmonic_i(static_cast(k), m, theta, phi)); } else { double plm = static_cast(::boost::math::legendre_p(k,abs(m),-cos(theta))); double mag = sqrt((2.0*k+1.0)/(4.0*itk::Math::pi)*::boost::math::factorial(k-abs(m))/::boost::math::factorial(k+abs(m)))*plm; if (m>0) return mag*static_cast(cos(m*phi)); else if (m==0) return mag; else return mag*static_cast(sin(-m*phi)); } return 0; } mitk::OdfImage::ItkOdfImageType::Pointer mitk::convert::GetItkOdfFromShImage(mitk::Image::Pointer mitkImage) { mitk::ShImage::Pointer mitkShImage = dynamic_cast(mitkImage.GetPointer()); if (mitkShImage.IsNull()) mitkThrow() << "Input image is not a SH image!"; mitk::OdfImage::ItkOdfImageType::Pointer output; switch (mitkShImage->ShOrder()) { case 2: { typedef itk::ShToOdfImageFilter< float, 2 > ShConverterType; typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New(); mitk::CastToItkImage(mitkImage, itkvol); typename ShConverterType::Pointer converter = ShConverterType::New(); converter->SetInput(itkvol); converter->Update(); output = converter->GetOutput(); break; } case 4: { typedef itk::ShToOdfImageFilter< float, 4 > ShConverterType; typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New(); mitk::CastToItkImage(mitkImage, itkvol); typename ShConverterType::Pointer converter = ShConverterType::New(); converter->SetInput(itkvol); converter->Update(); output = converter->GetOutput(); break; } case 6: { typedef itk::ShToOdfImageFilter< float, 6 > ShConverterType; typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New(); mitk::CastToItkImage(mitkImage, itkvol); typename ShConverterType::Pointer converter = ShConverterType::New(); converter->SetInput(itkvol); converter->Update(); output = converter->GetOutput(); break; } case 8: { typedef itk::ShToOdfImageFilter< float, 8 > ShConverterType; typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New(); mitk::CastToItkImage(mitkImage, itkvol); typename ShConverterType::Pointer converter = ShConverterType::New(); converter->SetInput(itkvol); converter->Update(); output = converter->GetOutput(); break; } case 10: { typedef itk::ShToOdfImageFilter< float, 10 > ShConverterType; typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New(); mitk::CastToItkImage(mitkImage, itkvol); typename ShConverterType::Pointer converter = ShConverterType::New(); converter->SetInput(itkvol); converter->Update(); output = converter->GetOutput(); break; } case 12: { typedef itk::ShToOdfImageFilter< float, 12 > ShConverterType; typename ShConverterType::InputImageType::Pointer itkvol = ShConverterType::InputImageType::New(); mitk::CastToItkImage(mitkImage, itkvol); typename ShConverterType::Pointer converter = ShConverterType::New(); converter->SetInput(itkvol); converter->Update(); output = converter->GetOutput(); break; } default: mitkThrow() << "SH orders higher than 12 are not supported!"; } return output; } mitk::OdfImage::Pointer mitk::convert::GetOdfFromShImage(mitk::Image::Pointer mitkImage) { mitk::OdfImage::Pointer image = mitk::OdfImage::New(); auto img = GetItkOdfFromShImage(mitkImage); image->InitializeByItk( img.GetPointer() ); image->SetVolume( img->GetBufferPointer() ); return image; } mitk::OdfImage::ItkOdfImageType::Pointer mitk::convert::GetItkOdfFromTensorImage(mitk::Image::Pointer mitkImage) { typedef itk::TensorImageToOdfImageFilter< float, float > FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput( GetItkTensorFromTensorImage(mitkImage) ); filter->Update(); return filter->GetOutput(); } mitk::TensorImage::ItkTensorImageType::Pointer mitk::convert::GetItkTensorFromTensorImage(mitk::Image::Pointer mitkImage) { typedef mitk::ImageToItk< mitk::TensorImage::ItkTensorImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(mitkImage); caster->Update(); return caster->GetOutput(); } mitk::PeakImage::ItkPeakImageType::Pointer mitk::convert::GetItkPeakFromPeakImage(mitk::Image::Pointer mitkImage) { typedef mitk::ImageToItk< mitk::PeakImage::ItkPeakImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(mitkImage); caster->SetCopyMemFlag(true); caster->Update(); return caster->GetOutput(); } mitk::OdfImage::Pointer mitk::convert::GetOdfFromTensorImage(mitk::Image::Pointer mitkImage) { mitk::OdfImage::Pointer image = mitk::OdfImage::New(); auto img = GetItkOdfFromTensorImage(mitkImage); image->InitializeByItk( img.GetPointer() ); image->SetVolume( img->GetBufferPointer() ); return image; } mitk::OdfImage::ItkOdfImageType::Pointer mitk::convert::GetItkOdfFromOdfImage(mitk::Image::Pointer mitkImage) { typedef mitk::ImageToItk< mitk::OdfImage::ItkOdfImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(mitkImage); caster->Update(); return caster->GetOutput(); } vnl_matrix mitk::sh::CalcShBasisForDirections(unsigned int sh_order, vnl_matrix U, bool mrtrix) { vnl_matrix sh_basis = vnl_matrix(U.cols(), (sh_order*sh_order + sh_order + 2)/2 + sh_order ); for(unsigned int i=0; i(sh_order); k+=2) { for(int m=-k; m<=k; m++) { int j = (k*k + k + 2)/2 + m - 1; double phi = U(0,i); double th = U(1,i); sh_basis(i,j) = mitk::sh::Yj(m,k,th,phi, mrtrix); } } } return sh_basis; } unsigned int mitk::sh::ShOrder(int num_coeffs) { int c=3, d=2-2*num_coeffs; int D = c*c-4*d; if (D>0) { int s = (-c+static_cast(sqrt(D)))/2; if (s<0) s = (-c-static_cast(sqrt(D)))/2; return static_cast(s); } else if (D==0) return static_cast(-c/2); return 0; } float mitk::sh::GetValue(const vnl_vector &coefficients, const int &sh_order, const double theta, const double phi, const bool mrtrix) { float val = 0; for(int k=0; k<=sh_order; k+=2) { for(int m=-k; m<=k; m++) { unsigned int j = static_cast((k*k + k + 2)/2 + m - 1); val += coefficients[j] * mitk::sh::Yj(m, k, theta, phi, mrtrix); } } return val; } float mitk::sh::GetValue(const vnl_vector &coefficients, const int &sh_order, const vnl_vector_fixed &dir, const bool mrtrix) { double spherical[3]; mitk::sh::Cart2Sph(dir[0], dir[1], dir[2], spherical); float val = 0; for(int k=0; k<=sh_order; k+=2) { for(int m=-k; m<=k; m++) { int j = (k*k + k + 2)/2 + m - 1; val += coefficients[j] * mitk::sh::Yj(m, k, spherical[1], spherical[0], mrtrix); } } return val; } //------------------------- gradients-function ------------------------------------ mitk::gradients::GradientDirectionContainerType::Pointer mitk::gradients::ReadBvalsBvecs(std::string bvals_file, std::string bvecs_file, double& reference_bval) { mitk::gradients::GradientDirectionContainerType::Pointer directioncontainer = mitk::gradients::GradientDirectionContainerType::New(); std::vector bvec_entries; if (!itksys::SystemTools::FileExists(bvecs_file)) mitkThrow() << "bvecs file not existing: " << bvecs_file; else { std::string line; std::ifstream myfile (bvecs_file.c_str()); if (myfile.is_open()) { while (std::getline(myfile, line)) { std::vector strs; boost::split(strs,line,boost::is_any_of("\t \n")); for (auto token : strs) { if (!token.empty()) { try { bvec_entries.push_back(boost::lexical_cast(token)); } catch(...) { mitkThrow() << "Encountered invalid bvecs file entry >" << token << "<"; } } } } myfile.close(); } else { mitkThrow() << "bvecs file could not be opened: " << bvals_file; } } reference_bval = -1; std::vector bval_entries; if (!itksys::SystemTools::FileExists(bvals_file)) mitkThrow() << "bvals file not existing: " << bvals_file; else { std::string line; std::ifstream myfile (bvals_file.c_str()); if (myfile.is_open()) { while (std::getline(myfile, line)) { std::vector strs; boost::split(strs,line,boost::is_any_of("\t \n")); for (auto token : strs) { if (!token.empty()) { try { bval_entries.push_back(boost::lexical_cast(token)); if (bval_entries.back()>reference_bval) reference_bval = bval_entries.back(); } catch(...) { mitkThrow() << "Encountered invalid bvals file entry >" << token << "<"; } } } } myfile.close(); } else { mitkThrow() << "bvals file could not be opened: " << bvals_file; } } for(unsigned int i=0; i 0) + if (reference_bval>0) { - vec.normalize(); - vec[0] = sqrt(factor)*vec[0]; - vec[1] = sqrt(factor)*vec[1]; - vec[2] = sqrt(factor)*vec[2]; + double factor = b_val/reference_bval; + if(vec.magnitude() > 0) + { + vec.normalize(); + vec[0] = sqrt(factor)*vec[0]; + vec[1] = sqrt(factor)*vec[1]; + vec[2] = sqrt(factor)*vec[2]; + } } directioncontainer->InsertElement(i,vec); } return directioncontainer; } void mitk::gradients::WriteBvalsBvecs(std::string bvals_file, std::string bvecs_file, GradientDirectionContainerType::Pointer gradients, double reference_bval) { std::ofstream myfile; myfile.open (bvals_file.c_str()); for(unsigned int i=0; iSize(); i++) { double twonorm = gradients->ElementAt(i).two_norm(); myfile << std::round(reference_bval*twonorm*twonorm) << " "; } myfile.close(); std::ofstream myfile2; myfile2.open (bvecs_file.c_str()); for(int j=0; j<3; j++) { for(unsigned int i=0; iSize(); i++) { GradientDirectionType direction = gradients->ElementAt(i); direction.normalize(); myfile2 << direction.get(j) << " "; } myfile2 << std::endl; } } std::vector mitk::gradients::GetAllUniqueDirections(const BValueMap & refBValueMap, GradientDirectionContainerType *refGradientsContainer ) { IndiciesVector directioncontainer; auto mapIterator = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator++; //skip bzero Values for( ; mapIterator != refBValueMap.end(); mapIterator++){ IndiciesVector currentShell = mapIterator->second; while(currentShell.size()>0) { unsigned int wntIndex = currentShell.back(); currentShell.pop_back(); auto containerIt = directioncontainer.begin(); bool directionExist = false; while(containerIt != directioncontainer.end()) { if (fabs(dot_product(refGradientsContainer->ElementAt(*containerIt), refGradientsContainer->ElementAt(wntIndex))) > 0.9998) { directionExist = true; break; } containerIt++; } if(!directionExist) { directioncontainer.push_back(wntIndex); } } } return directioncontainer; } bool mitk::gradients::CheckForDifferingShellDirections(const BValueMap & refBValueMap, GradientDirectionContainerType::ConstPointer refGradientsContainer) { auto mapIterator = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator++; //skip bzero Values for( ; mapIterator != refBValueMap.end(); mapIterator++){ auto mapIterator_2 = refBValueMap.begin(); if(refBValueMap.find(0) != refBValueMap.end() && refBValueMap.size() > 1) mapIterator_2++; //skip bzero Values for( ; mapIterator_2 != refBValueMap.end(); mapIterator_2++){ if(mapIterator_2 == mapIterator) continue; IndiciesVector currentShell = mapIterator->second; IndiciesVector testShell = mapIterator_2->second; for (unsigned int i = 0; i< currentShell.size(); i++) if (fabs(dot_product(refGradientsContainer->ElementAt(currentShell[i]), refGradientsContainer->ElementAt(testShell[i]))) <= 0.9998) { return true; } } } return false; } vnl_matrix mitk::gradients::ComputeSphericalFromCartesian(const IndiciesVector & refShell, const GradientDirectionContainerType * refGradientsContainer) { vnl_matrix Q(3, refShell.size()); Q.fill(0.0); for(unsigned int i = 0; i < refShell.size(); i++) { GradientDirectionType dir = refGradientsContainer->ElementAt(refShell[i]); double x = dir.normalize().get(0); double y = dir.normalize().get(1); double z = dir.normalize().get(2); double cart[3]; mitk::sh::Cart2Sph(x,y,z,cart); Q(0,i) = cart[0]; Q(1,i) = cart[1]; Q(2,i) = cart[2]; } return Q; } vnl_matrix mitk::gradients::ComputeSphericalHarmonicsBasis(const vnl_matrix & QBallReference, const unsigned int & LOrder) { vnl_matrix SHBasisOutput(QBallReference.cols(), (LOrder+1)*(LOrder+2)*0.5); SHBasisOutput.fill(0.0); for(unsigned int i=0; i 1){ mapIterator++; //skip bzero Values vnl_vector_fixed vec; vec.fill(0.0); directioncontainer->push_back(vec); } for( ; mapIterator != bValueMap.end(); mapIterator++){ IndiciesVector currentShell = mapIterator->second; while(currentShell.size()>0) { unsigned int wntIndex = currentShell.back(); currentShell.pop_back(); mitk::gradients::GradientDirectionContainerType::Iterator containerIt = directioncontainer->Begin(); bool directionExist = false; while(containerIt != directioncontainer->End()) { if (fabs(dot_product(containerIt.Value(), origninalGradentcontainer->ElementAt(wntIndex))) > 0.9998) { directionExist = true; break; } containerIt++; } if(!directionExist) { GradientDirectionType dir(origninalGradentcontainer->ElementAt(wntIndex)); directioncontainer->push_back(dir.normalize()); } } } return directioncontainer; } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.cpp index 94f25ae860..bae0ea1b8d 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.cpp @@ -1,34 +1,27 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTrackingDataHandler.h" namespace mitk { TrackingDataHandler::TrackingDataHandler() - : m_AngularThreshold(0.7) - , m_Interpolate(true) - , m_FlipX(false) - , m_FlipY(false) - , m_FlipZ(false) - , m_Mode(MODE::DETERMINISTIC) - , m_RngItk(ItkRngType::New()) + : m_RngItk(ItkRngType::New()) , m_NeedsDataInit(true) - , m_Random(true) { } } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.h b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.h index 886a8792b3..71d473df0c 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.h @@ -1,123 +1,122 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _TrackingDataHandler #define _TrackingDataHandler #include #include #include #include #include #include #include #include #include #include +#include namespace mitk { /** * \brief Abstract class for tracking handler. A tracking handler deals with determining the next progression direction of a streamline fiber. There are different handlers for tensor images, peak images, ... */ class MITKFIBERTRACKING_EXPORT TrackingDataHandler { public: - enum MODE { - DETERMINISTIC, - PROBABILISTIC - }; - TrackingDataHandler(); virtual ~TrackingDataHandler(){} typedef itk::Statistics::MersenneTwisterRandomVariateGenerator ItkRngType; typedef boost::mt19937 BoostRngType; typedef itk::Image ItkUcharImgType; typedef itk::Image ItkShortImgType; typedef itk::Image ItkFloatImgType; typedef itk::Image ItkDoubleImgType; typedef vnl_vector_fixed< float, 3 > TrackingDirectionType; + typedef mitk::StreamlineTractographyParameters::MODE MODE; virtual TrackingDirectionType ProposeDirection(const itk::Point& pos, std::deque< TrackingDirectionType >& olddirs, itk::Index<3>& oldIndex) = 0; ///< predicts next progression direction at the given position virtual void InitForTracking() = 0; virtual itk::Vector GetSpacing() = 0; virtual itk::Point GetOrigin() = 0; virtual itk::Matrix GetDirection() = 0; virtual itk::ImageRegion<3> GetLargestPossibleRegion() = 0; virtual bool WorldToIndex(itk::Point& pos, itk::Index<3>& index) = 0; - virtual void SetMode(MODE m) = 0; - MODE GetMode() const { return m_Mode; } - - void SetInterpolate( bool interpolate ){ m_Interpolate = interpolate; } - bool GetInterpolate() const { return m_Interpolate; } - void SetAngularThreshold( float a ){ m_AngularThreshold = a; } - void SetFlipX( bool f ){ m_FlipX = f; } - void SetFlipY( bool f ){ m_FlipY = f; } - void SetFlipZ( bool f ){ m_FlipZ = f; } - void SetRandom( bool random ) + void SetParameters(std::shared_ptr< mitk::StreamlineTractographyParameters > parameters) { - m_Random = random; - if (!random) + m_Parameters = parameters; + + if (m_Parameters->m_FixRandomSeed) { m_Rng.seed(0); std::srand(0); m_RngItk->SetSeed(0); } else { m_Rng.seed(); m_RngItk->SetSeed(); std::srand(std::time(nullptr)); } } double GetRandDouble(const double & a, const double & b) { return m_RngItk->GetUniformVariate(a, b); } - bool GetFlipX() const { return m_FlipX; } - bool GetFlipY() const { return m_FlipY; } - bool GetFlipZ() const { return m_FlipZ; } protected: - float m_AngularThreshold; - bool m_Interpolate; - bool m_FlipX; - bool m_FlipY; - bool m_FlipZ; - MODE m_Mode; + void CalculateMinVoxelSize() + { + itk::Vector< double, 3 > imageSpacing = this->GetSpacing(); + float minVoxelSize = 0; + if(imageSpacing[0](imageSpacing[0]); + else if (imageSpacing[1] < imageSpacing[2]) + minVoxelSize = static_cast(imageSpacing[1]); + else + minVoxelSize = static_cast(imageSpacing[2]); + + if (m_Parameters==nullptr) + mitkThrow() << "No tractography parameter opbect set!"; + + m_Parameters->SetMinVoxelSizeMm(minVoxelSize); + } + BoostRngType m_Rng; ItkRngType::Pointer m_RngItk; bool m_NeedsDataInit; - bool m_Random; + std::shared_ptr< mitk::StreamlineTractographyParameters > m_Parameters; void DataModified() { m_NeedsDataInit = true; } + vnl_matrix_fixed m_FloatImageRotation; + }; } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp index 2fed13346f..503a0143c5 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp @@ -1,294 +1,307 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTrackingHandlerOdf.h" #include #include #include #include #include namespace mitk { TrackingHandlerOdf::TrackingHandlerOdf() - : m_GfaThreshold(0.2) - , m_OdfThreshold(0.1) - , m_SharpenOdfs(false) - , m_NumProbSamples(1) + : m_NumProbSamples(1) , m_OdfFromTensor(false) { m_GfaInterpolator = itk::LinearInterpolateImageFunction< itk::Image< float, 3 >, float >::New(); m_OdfInterpolator = itk::LinearInterpolateImageFunction< itk::Image< ItkOdfImageType::PixelType, 3 >, float >::New(); } TrackingHandlerOdf::~TrackingHandlerOdf() { } bool TrackingHandlerOdf::WorldToIndex(itk::Point& pos, itk::Index<3>& index) { m_OdfImage->TransformPhysicalPointToIndex(pos, index); return m_OdfImage->GetLargestPossibleRegion().IsInside(index); } void TrackingHandlerOdf::InitForTracking() { MITK_INFO << "Initializing ODF tracker."; if (m_NeedsDataInit) { m_OdfHemisphereIndices.clear(); itk::OrientationDistributionFunction< float, ODF_SAMPLING_SIZE > odf; vnl_vector_fixed ref; ref.fill(0); ref[0]=1; for (int i=0; i0) m_OdfHemisphereIndices.push_back(i); m_OdfFloatDirs.set_size(m_OdfHemisphereIndices.size(), 3); + auto double_dir = m_OdfImage->GetDirection().GetVnlMatrix(); + for (int r=0; r<3; r++) + for (int c=0; c<3; c++) + { + m_FloatImageRotation[r][c] = double_dir[r][c]; + } + for (unsigned int i=0; i GfaFilterType; GfaFilterType::Pointer gfaFilter = GfaFilterType::New(); gfaFilter->SetInput(m_OdfImage); gfaFilter->SetComputationMethod(GfaFilterType::GFA_STANDARD); gfaFilter->Update(); m_GfaImage = gfaFilter->GetOutput(); } + this->CalculateMinVoxelSize(); m_NeedsDataInit = false; } + if (m_OdfFromTensor) + { + m_Parameters->m_OdfCutoff = 0; + m_Parameters->m_SharpenOdfs = false; + } + m_GfaInterpolator->SetInputImage(m_GfaImage); m_OdfInterpolator->SetInputImage(m_OdfImage); - std::cout << "TrackingHandlerOdf - GFA threshold: " << m_GfaThreshold << std::endl; - std::cout << "TrackingHandlerOdf - ODF threshold: " << m_OdfThreshold << std::endl; - if (m_SharpenOdfs) + std::cout << "TrackingHandlerOdf - GFA threshold: " << m_Parameters->m_Cutoff << std::endl; + std::cout << "TrackingHandlerOdf - ODF threshold: " << m_Parameters->m_OdfCutoff << std::endl; + if (m_Parameters->m_SharpenOdfs) std::cout << "TrackingHandlerOdf - Sharpening ODfs" << std::endl; } int TrackingHandlerOdf::SampleOdf(vnl_vector< float >& probs, vnl_vector< float >& angles) { boost::random::discrete_distribution dist(probs.begin(), probs.end()); int sampled_idx = 0; int max_sample_idx = -1; float max_prob = 0; int trials = 0; for (int i=0; i> sampler(m_Rng, dist); sampled_idx = sampler(); } - if (probs[sampled_idx]>max_prob && probs[sampled_idx]>m_OdfThreshold && fabs(angles[sampled_idx])>=m_AngularThreshold) + if (probs[sampled_idx]>max_prob && probs[sampled_idx]>m_Parameters->m_OdfCutoff && fabs(angles[sampled_idx])>=m_Parameters->GetAngularThresholdDot()) { max_prob = probs[sampled_idx]; max_sample_idx = sampled_idx; } - else if ( (probs[sampled_idx]<=m_OdfThreshold || fabs(angles[sampled_idx])m_OdfCutoff || fabs(angles[sampled_idx])GetAngularThresholdDot()) && trials<50) // we allow 50 trials to exceed the ODF threshold i--; } return max_sample_idx; } void TrackingHandlerOdf::SetIsOdfFromTensor(bool OdfFromTensor) { m_OdfFromTensor = OdfFromTensor; } bool TrackingHandlerOdf::GetIsOdfFromTensor() const { return m_OdfFromTensor; } vnl_vector_fixed TrackingHandlerOdf::ProposeDirection(const itk::Point& pos, std::deque >& olddirs, itk::Index<3>& oldIndex) { vnl_vector_fixed output_direction; output_direction.fill(0); itk::Index<3> idx; m_OdfImage->TransformPhysicalPointToIndex(pos, idx); if ( !m_OdfImage->GetLargestPossibleRegion().IsInside(idx) ) return output_direction; // check GFA threshold for termination - float gfa = mitk::imv::GetImageValue(pos, m_Interpolate, m_GfaInterpolator); - if (gfa(pos, m_Parameters->m_InterpolateTractographyData, m_GfaInterpolator); + if (gfam_Cutoff) return output_direction; vnl_vector_fixed last_dir; if (!olddirs.empty()) last_dir = olddirs.back(); - if (!m_Interpolate && oldIndex==idx) + if (!m_Parameters->m_InterpolateTractographyData && oldIndex==idx) return last_dir; - ItkOdfImageType::PixelType odf_values = mitk::imv::GetImageValue(pos, m_Interpolate, m_OdfInterpolator); + ItkOdfImageType::PixelType odf_values = mitk::imv::GetImageValue(pos, m_Parameters->m_InterpolateTractographyData, m_OdfInterpolator); vnl_vector< float > probs; probs.set_size(m_OdfHemisphereIndices.size()); vnl_vector< float > angles; angles.set_size(m_OdfHemisphereIndices.size()); angles.fill(1.0); // Find ODF maximum and remove <0 values float max_odf_val = 0; float min_odf_val = 999; int max_idx_d = -1; int c = 0; for (int i : m_OdfHemisphereIndices) { if (odf_values[i]<0) odf_values[i] = 0; if (odf_values[i]>max_odf_val) { max_odf_val = odf_values[i]; max_idx_d = c; } if (odf_values[i]m_SharpenOdfs) { // sharpen ODF probs -= min_odf_val; probs /= (max_odf_val-min_odf_val); for (unsigned int i=0; i0) { probs /= odf_sum; max_odf_val /= odf_sum; } } // no previous direction - if (max_odf_val>m_OdfThreshold && (olddirs.empty() || last_dir.magnitude()<=0.5)) + if (max_odf_val>m_Parameters->m_OdfCutoff && (olddirs.empty() || last_dir.magnitude()<=0.5)) { - if (m_Mode==MODE::DETERMINISTIC) // return maximum peak + if (m_Parameters->m_Mode==MODE::DETERMINISTIC) // return maximum peak { output_direction = m_OdfFloatDirs.get_row(max_idx_d); return output_direction * max_odf_val; } - else if (m_Mode==MODE::PROBABILISTIC) // sample from complete ODF + else if (m_Parameters->m_Mode==MODE::PROBABILISTIC) // sample from complete ODF { int max_sample_idx = SampleOdf(probs, angles); if (max_sample_idx>=0) output_direction = m_OdfFloatDirs.get_row(max_sample_idx) * probs[max_sample_idx]; return output_direction; } } - else if (max_odf_val<=m_OdfThreshold) // return (0,0,0) + else if (max_odf_val<=m_Parameters->m_OdfCutoff) // return (0,0,0) { return output_direction; } // correct previous direction - if (m_FlipX) + if (m_Parameters->m_FlipX) last_dir[0] *= -1; - if (m_FlipY) + if (m_Parameters->m_FlipY) last_dir[1] *= -1; - if (m_FlipZ) + if (m_Parameters->m_FlipZ) last_dir[2] *= -1; // calculate angles between previous direction and ODF directions angles = m_OdfFloatDirs*last_dir; float probs_sum = 0; float max_prob = 0; for (unsigned int i=0; imax_prob && odf_val>m_OdfThreshold) + if (m_Parameters->m_Mode==MODE::DETERMINISTIC && odf_val>max_prob && odf_val>m_Parameters->m_OdfCutoff) { // use maximum peak of the ODF weighted with the directional prior max_prob = odf_val; vnl_vector_fixed d = m_OdfFloatDirs.get_row(i); if (angle<0) d *= -1; output_direction = odf_val*d; } - else if (m_Mode==MODE::PROBABILISTIC) + else if (m_Parameters->m_Mode==MODE::PROBABILISTIC) { // update ODF probabilties with the ODF values pow(abs_angle, m_DirPriorPower) probs[i] = odf_val; probs_sum += probs[i]; } } // do probabilistic sampling - if (m_Mode==MODE::PROBABILISTIC && probs_sum>0.0001) + if (m_Parameters->m_Mode==MODE::PROBABILISTIC && probs_sum>0.0001) { int max_sample_idx = SampleOdf(probs, angles); if (max_sample_idx>=0) { output_direction = m_OdfFloatDirs.get_row(max_sample_idx); if (angles[max_sample_idx]<0) output_direction *= -1; output_direction *= probs[max_sample_idx]; } } // check hard angular threshold float mag = output_direction.magnitude(); if (mag>=0.0001) { output_direction.normalize(); float a = dot_product(output_direction, last_dir); - if (aGetAngularThresholdDot()) output_direction.fill(0); } else output_direction.fill(0); - if (m_FlipX) + if (m_Parameters->m_FlipX) output_direction[0] *= -1; - if (m_FlipY) + if (m_Parameters->m_FlipY) output_direction[1] *= -1; - if (m_FlipZ) + if (m_Parameters->m_FlipZ) output_direction[2] *= -1; + if (m_Parameters->m_ApplyDirectionMatrix) + output_direction = m_FloatImageRotation*output_direction; return output_direction; } void TrackingHandlerOdf::SetNumProbSamples(int NumProbSamples) { m_NumProbSamples = NumProbSamples; } } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.h b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.h index 593c02520e..4a70fd4f0c 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.h @@ -1,87 +1,80 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _TrackingHandlerOdf #define _TrackingHandlerOdf #include "mitkTrackingDataHandler.h" #include #include #include #include namespace mitk { /** * \brief Enables streamline tracking on sampled ODF images. */ class MITKFIBERTRACKING_EXPORT TrackingHandlerOdf : public TrackingDataHandler { public: TrackingHandlerOdf(); ~TrackingHandlerOdf() override; typedef OdfImage::ItkOdfImageType ItkOdfImageType; typedef itk::Image< vnl_vector_fixed, 3> ItkPDImgType; void InitForTracking() override; ///< calls InputDataValidForTracking() and creates feature images vnl_vector_fixed ProposeDirection(const itk::Point& pos, std::deque< vnl_vector_fixed >& olddirs, itk::Index<3>& oldIndex) override; ///< predicts next progression direction at the given position bool WorldToIndex(itk::Point& pos, itk::Index<3>& index) override; - void SetSharpenOdfs(bool doSharpen) { m_SharpenOdfs=doSharpen; } - void SetOdfThreshold(float odfThreshold){ m_OdfThreshold = odfThreshold; } - void SetGfaThreshold(float gfaThreshold){ m_GfaThreshold = gfaThreshold; } void SetOdfImage( ItkOdfImageType::Pointer img ){ m_OdfImage = img; DataModified(); } void SetGfaImage( ItkFloatImgType::Pointer img ){ m_GfaImage = img; DataModified(); } - void SetMode( MODE m ) override{ m_Mode = m; } ItkUcharImgType::SpacingType GetSpacing() override{ return m_OdfImage->GetSpacing(); } itk::Point GetOrigin() override{ return m_OdfImage->GetOrigin(); } ItkUcharImgType::DirectionType GetDirection() override{ return m_OdfImage->GetDirection(); } ItkUcharImgType::RegionType GetLargestPossibleRegion() override{ return m_OdfImage->GetLargestPossibleRegion(); } int OdfPower() const; void SetNumProbSamples(int NumProbSamples); bool GetIsOdfFromTensor() const; void SetIsOdfFromTensor(bool OdfFromTensor); protected: int SampleOdf(vnl_vector< float >& probs, vnl_vector< float >& angles); - float m_GfaThreshold; - float m_OdfThreshold; - bool m_SharpenOdfs; ItkFloatImgType::Pointer m_GfaImage; ///< GFA image used to determine streamline termination. ItkOdfImageType::Pointer m_OdfImage; ///< Input odf image. ItkOdfImageType::Pointer m_WorkingOdfImage; ///< Modified odf image. std::vector< int > m_OdfHemisphereIndices; vnl_matrix< float > m_OdfFloatDirs; int m_NumProbSamples; bool m_OdfFromTensor; itk::LinearInterpolateImageFunction< itk::Image< float, 3 >, float >::Pointer m_GfaInterpolator; itk::LinearInterpolateImageFunction< itk::Image< ItkOdfImageType::PixelType, 3 >, float >::Pointer m_OdfInterpolator; }; } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.cpp index 5243db3e71..efa67a9145 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.cpp @@ -1,295 +1,306 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTrackingHandlerPeaks.h" namespace mitk { TrackingHandlerPeaks::TrackingHandlerPeaks() - : m_PeakThreshold(0.1) - , m_ApplyDirectionMatrix(false) { } TrackingHandlerPeaks::~TrackingHandlerPeaks() { } bool TrackingHandlerPeaks::WorldToIndex(itk::Point& pos, itk::Index<3>& index) { m_DummyImage->TransformPhysicalPointToIndex(pos, index); return m_DummyImage->GetLargestPossibleRegion().IsInside(index); } void TrackingHandlerPeaks::InitForTracking() { MITK_INFO << "Initializing peak tracker."; if (m_NeedsDataInit) { itk::Vector spacing4 = m_PeakImage->GetSpacing(); itk::Point origin4 = m_PeakImage->GetOrigin(); itk::Matrix direction4 = m_PeakImage->GetDirection(); itk::ImageRegion<4> imageRegion4 = m_PeakImage->GetLargestPossibleRegion(); spacing3[0] = spacing4[0]; spacing3[1] = spacing4[1]; spacing3[2] = spacing4[2]; origin3[0] = origin4[0]; origin3[1] = origin4[1]; origin3[2] = origin4[2]; for (int r=0; r<3; r++) for (int c=0; c<3; c++) { direction3[r][c] = direction4[r][c]; m_FloatImageRotation[r][c] = direction4[r][c]; } imageRegion3.SetSize(0, imageRegion4.GetSize()[0]); imageRegion3.SetSize(1, imageRegion4.GetSize()[1]); imageRegion3.SetSize(2, imageRegion4.GetSize()[2]); m_DummyImage = ItkUcharImgType::New(); m_DummyImage->SetSpacing( spacing3 ); m_DummyImage->SetOrigin( origin3 ); m_DummyImage->SetDirection( direction3 ); m_DummyImage->SetRegions( imageRegion3 ); m_DummyImage->Allocate(); m_DummyImage->FillBuffer(0.0); m_NumDirs = imageRegion4.GetSize(3)/3; + + this->CalculateMinVoxelSize(); m_NeedsDataInit = false; } - std::cout << "TrackingHandlerPeaks - Peak threshold: " << m_PeakThreshold << std::endl; + std::cout << "TrackingHandlerPeaks - Peak threshold: " << m_Parameters->m_Cutoff << std::endl; + if (m_Parameters->m_Mode == MODE::PROBABILISTIC) + std::cout << "TrackingHandlerPeaks - Peak jitter: " << m_Parameters->m_PeakJitter << std::endl; } vnl_vector_fixed TrackingHandlerPeaks::GetMatchingDirection(itk::Index<3> idx3, vnl_vector_fixed& oldDir) { vnl_vector_fixed out_dir; out_dir.fill(0); float angle = 0; float mag = oldDir.magnitude(); if (magm_FixRandomSeed) { // try m_NumDirs times to get a non-zero random direction for (int j=0; jGetIntegerVariate(m_NumDirs-1); out_dir = GetDirection(idx3, i); if (out_dir.magnitude()>mitk::eps) { oldDir[0] = out_dir[0]; oldDir[1] = out_dir[1]; oldDir[2] = out_dir[2]; found = true; break; } } } if (!found) { // if you didn't find a non-zero random direction, take first non-zero direction you find for (int i=0; imitk::eps) { oldDir[0] = out_dir[0]; oldDir[1] = out_dir[1]; oldDir[2] = out_dir[2]; break; } } } } else { for (int i=0; i dir = GetDirection(idx3, i); mag = dir.magnitude(); if (mag>mitk::eps) dir.normalize(); float a = dot_product(dir, oldDir); if (fabs(a)>angle) { angle = fabs(a); if (a<0) out_dir = -dir; else out_dir = dir; out_dir *= mag; out_dir *= angle; // shrink contribution of direction if is less parallel to previous direction } } } return out_dir; } vnl_vector_fixed TrackingHandlerPeaks::GetDirection(itk::Index<3> idx3, int dirIdx) { vnl_vector_fixed dir; dir.fill(0.0); if ( !m_DummyImage->GetLargestPossibleRegion().IsInside(idx3) ) return dir; PeakImgType::IndexType idx4; idx4.SetElement(0,idx3[0]); idx4.SetElement(1,idx3[1]); idx4.SetElement(2,idx3[2]); for (int k=0; k<3; k++) { idx4.SetElement(3, dirIdx*3 + k); dir[k] = m_PeakImage->GetPixel(idx4); } - if (m_FlipX) + if (m_Parameters->m_FlipX) dir[0] *= -1; - if (m_FlipY) + if (m_Parameters->m_FlipY) dir[1] *= -1; - if (m_FlipZ) + if (m_Parameters->m_FlipZ) dir[2] *= -1; - if (m_ApplyDirectionMatrix) + if (m_Parameters->m_ApplyDirectionMatrix) dir = m_FloatImageRotation*dir; return dir; } vnl_vector_fixed TrackingHandlerPeaks::GetDirection(itk::Point itkP, bool interpolate, vnl_vector_fixed oldDir){ // transform physical point to index coordinates itk::Index<3> idx3; itk::ContinuousIndex< float, 3> cIdx; m_DummyImage->TransformPhysicalPointToIndex(itkP, idx3); m_DummyImage->TransformPhysicalPointToContinuousIndex(itkP, cIdx); vnl_vector_fixed dir; dir.fill(0.0); if ( !m_DummyImage->GetLargestPossibleRegion().IsInside(idx3) ) return dir; if (interpolate) { float frac_x = cIdx[0] - idx3[0]; float frac_y = cIdx[1] - idx3[1]; float frac_z = cIdx[2] - idx3[2]; if (frac_x<0) { idx3[0] -= 1; frac_x += 1; } if (frac_y<0) { idx3[1] -= 1; frac_y += 1; } if (frac_z<0) { idx3[2] -= 1; frac_z += 1; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? if (idx3[0] >= 0 && idx3[0] < static_cast(m_DummyImage->GetLargestPossibleRegion().GetSize(0) - 1) && idx3[1] >= 0 && idx3[1] < static_cast(m_DummyImage->GetLargestPossibleRegion().GetSize(1) - 1) && idx3[2] >= 0 && idx3[2] < static_cast(m_DummyImage->GetLargestPossibleRegion().GetSize(2) - 1)) { // trilinear interpolation vnl_vector_fixed interpWeights; interpWeights[0] = ( frac_x)*( frac_y)*( frac_z); interpWeights[1] = (1-frac_x)*( frac_y)*( frac_z); interpWeights[2] = ( frac_x)*(1-frac_y)*( frac_z); interpWeights[3] = ( frac_x)*( frac_y)*(1-frac_z); interpWeights[4] = (1-frac_x)*(1-frac_y)*( frac_z); interpWeights[5] = ( frac_x)*(1-frac_y)*(1-frac_z); interpWeights[6] = (1-frac_x)*( frac_y)*(1-frac_z); interpWeights[7] = (1-frac_x)*(1-frac_y)*(1-frac_z); dir = GetMatchingDirection(idx3, oldDir) * interpWeights[0]; itk::Index<3> tmpIdx = idx3; tmpIdx[0]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[1]; tmpIdx = idx3; tmpIdx[1]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[2]; tmpIdx = idx3; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[3]; tmpIdx = idx3; tmpIdx[0]++; tmpIdx[1]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[4]; tmpIdx = idx3; tmpIdx[1]++; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[5]; tmpIdx = idx3; tmpIdx[2]++; tmpIdx[0]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[6]; tmpIdx = idx3; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir) * interpWeights[7]; } } else dir = GetMatchingDirection(idx3, oldDir); return dir; } vnl_vector_fixed TrackingHandlerPeaks::ProposeDirection(const itk::Point& pos, std::deque >& olddirs, itk::Index<3>& oldIndex) { // CHECK: wann wird wo normalisiert vnl_vector_fixed output_direction; output_direction.fill(0); itk::Index<3> index; m_DummyImage->TransformPhysicalPointToIndex(pos, index); vnl_vector_fixed oldDir; oldDir.fill(0.0); if (!olddirs.empty()) oldDir = olddirs.back(); float old_mag = oldDir.magnitude(); - if (!m_Interpolate && oldIndex==index) + if (!m_Parameters->m_InterpolateTractographyData && oldIndex==index) return oldDir; - output_direction = GetDirection(pos, m_Interpolate, oldDir); + output_direction = GetDirection(pos, m_Parameters->m_InterpolateTractographyData, oldDir); float mag = output_direction.magnitude(); - if (mag>=m_PeakThreshold) + if (mag>=m_Parameters->m_Cutoff) { + if (m_Parameters->m_Mode == MODE::PROBABILISTIC) + { + output_direction[0] += this->m_RngItk->GetNormalVariate(0, fabs(output_direction[0])*m_Parameters->m_PeakJitter); + output_direction[1] += this->m_RngItk->GetNormalVariate(0, fabs(output_direction[1])*m_Parameters->m_PeakJitter); + output_direction[2] += this->m_RngItk->GetNormalVariate(0, fabs(output_direction[2])*m_Parameters->m_PeakJitter); + mag = output_direction.magnitude(); + } + output_direction.normalize(); + float a = 1; if (old_mag>0.5) a = dot_product(output_direction, oldDir); - if (a>=m_AngularThreshold) + if (a>=m_Parameters->GetAngularThresholdDot()) output_direction *= mag; else output_direction.fill(0); } else output_direction.fill(0); return output_direction; } } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.h b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.h index 42c7288777..6143ac66f2 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.h @@ -1,84 +1,71 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _TrackingHandlerPeaks #define _TrackingHandlerPeaks #include "mitkTrackingDataHandler.h" #include #include namespace mitk { /** * \brief Enables deterministic streamline tracking on MRtrix style peak images (4D float images) */ class MITKFIBERTRACKING_EXPORT TrackingHandlerPeaks : public TrackingDataHandler { public: TrackingHandlerPeaks(); ~TrackingHandlerPeaks() override; typedef itk::Image< float, 4 > PeakImgType; ///< MRtrix peak image type void InitForTracking() override; ///< calls InputDataValidForTracking() and creates feature images vnl_vector_fixed ProposeDirection(const itk::Point& pos, std::deque< vnl_vector_fixed >& olddirs, itk::Index<3>& oldIndex) override; ///< predicts next progression direction at the given position bool WorldToIndex(itk::Point& pos, itk::Index<3>& index) override; - void SetPeakThreshold(float thr){ m_PeakThreshold = thr; } void SetPeakImage( PeakImgType::Pointer image ){ m_PeakImage = image; DataModified(); } - void SetApplyDirectionMatrix( bool applyDirectionMatrix ){ m_ApplyDirectionMatrix = applyDirectionMatrix; } itk::Vector GetSpacing() override{ return spacing3; } itk::Point GetOrigin() override{ return origin3; } itk::Matrix GetDirection() override{ return direction3; } itk::ImageRegion<3> GetLargestPossibleRegion() override{ return imageRegion3; } - void SetMode( MODE m ) override - { - if (m==MODE::DETERMINISTIC) - m_Mode = m; - else - mitkThrow() << "Peak tracker is only implemented for deterministic mode."; - } protected: vnl_vector_fixed GetDirection(itk::Point itkP, bool interpolate, vnl_vector_fixed oldDir); vnl_vector_fixed GetMatchingDirection(itk::Index<3> idx3, vnl_vector_fixed& oldDir); vnl_vector_fixed GetDirection(itk::Index<3> idx3, int dirIdx); PeakImgType::ConstPointer m_PeakImage; - float m_PeakThreshold; int m_NumDirs; itk::Vector spacing3; itk::Point origin3; itk::Matrix direction3; itk::ImageRegion<3> imageRegion3; - vnl_matrix_fixed m_FloatImageRotation; ItkUcharImgType::Pointer m_DummyImage; - - bool m_ApplyDirectionMatrix; }; } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.cpp index dfeee974d3..f8375e26d6 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.cpp @@ -1,900 +1,909 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _TrackingForestHandler_cpp #define _TrackingForestHandler_cpp #include "mitkTrackingHandlerRandomForest.h" #include #include namespace mitk { template< int ShOrder, int NumberOfSignalFeatures > TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::TrackingHandlerRandomForest() : m_WmSampleDistance(-1) , m_NumTrees(30) , m_MaxTreeDepth(25) , m_SampleFraction(1.0) , m_NumberOfSamples(0) , m_GmSamplesPerVoxel(-1) - , m_NumPreviousDirections(1) , m_BidirectionalFiberSampling(false) , m_ZeroDirWmFeatures(true) , m_MaxNumWmSamples(-1) { vnl_vector_fixed ref; ref.fill(0); ref[0]=1; itk::OrientationDistributionFunction< float, 200 > odf; m_DirectionContainer.clear(); for (unsigned int i = 0; i odf_dir; odf_dir[0] = odf.GetDirection(i)[0]; odf_dir[1] = odf.GetDirection(i)[1]; odf_dir[2] = odf.GetDirection(i)[2]; if (dot_product(ref, odf_dir)>0) // only used directions on one hemisphere m_DirectionContainer.push_back(odf_dir); // store indices for later mapping the classifier output to the actual direction } m_OdfFloatDirs.set_size(m_DirectionContainer.size(), 3); for (unsigned int i=0; i TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::~TrackingHandlerRandomForest() { } template< int ShOrder, int NumberOfSignalFeatures > bool TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::WorldToIndex(itk::Point& pos, itk::Index<3>& index) { m_DwiFeatureImages.at(0)->TransformPhysicalPointToIndex(pos, index); return m_DwiFeatureImages.at(0)->GetLargestPossibleRegion().IsInside(index); } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InputDataValidForTracking() { if (m_InputDwis.empty()) mitkThrow() << "No diffusion-weighted images set!"; if (!IsForestValid()) mitkThrow() << "No or invalid random forest detected!"; } template< int ShOrder, int NumberOfSignalFeatures> template typename std::enable_if< NumberOfSignalFeatures <=99, T >::type TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InitDwiImageFeatures(mitk::Image::Pointer mitk_dwi) { MITK_INFO << "Calculating spherical harmonics features"; typedef itk::AnalyticalDiffusionQballReconstructionImageFilter InterpolationFilterType; typename InterpolationFilterType::Pointer filter = InterpolationFilterType::New(); filter->SetBValue(mitk::DiffusionPropertyHelper::GetReferenceBValue(mitk_dwi)); filter->SetGradientImage( mitk::DiffusionPropertyHelper::GetOriginalGradientContainer(mitk_dwi), mitk::DiffusionPropertyHelper::GetItkVectorImage(mitk_dwi) ); filter->SetLambda(0.006); filter->SetNormalizationMethod(InterpolationFilterType::QBAR_RAW_SIGNAL); filter->Update(); m_DwiFeatureImages.push_back(filter->GetCoefficientImage()); return true; } template< int ShOrder, int NumberOfSignalFeatures> template typename std::enable_if< NumberOfSignalFeatures >=100, T >::type TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InitDwiImageFeatures(mitk::Image::Pointer mitk_dwi) { MITK_INFO << "Interpolating raw dwi signal features"; typedef itk::AnalyticalDiffusionQballReconstructionImageFilter InterpolationFilterType; typename InterpolationFilterType::Pointer filter = InterpolationFilterType::New(); filter->SetBValue(mitk::DiffusionPropertyHelper::GetReferenceBValue(mitk_dwi)); filter->SetGradientImage( mitk::DiffusionPropertyHelper::GetOriginalGradientContainer(mitk_dwi), mitk::DiffusionPropertyHelper::GetItkVectorImage(mitk_dwi) ); filter->SetLambda(0.006); filter->SetNormalizationMethod(InterpolationFilterType::QBAR_RAW_SIGNAL); filter->Update(); typename DwiFeatureImageType::Pointer dwiFeatureImage = DwiFeatureImageType::New(); dwiFeatureImage->SetSpacing(filter->GetOutput()->GetSpacing()); dwiFeatureImage->SetOrigin(filter->GetOutput()->GetOrigin()); dwiFeatureImage->SetDirection(filter->GetOutput()->GetDirection()); dwiFeatureImage->SetLargestPossibleRegion(filter->GetOutput()->GetLargestPossibleRegion()); dwiFeatureImage->SetBufferedRegion(filter->GetOutput()->GetLargestPossibleRegion()); dwiFeatureImage->SetRequestedRegion(filter->GetOutput()->GetLargestPossibleRegion()); dwiFeatureImage->Allocate(); // get signal values and store them in the feature image vnl_vector_fixed ref; ref.fill(0); ref[0]=1; itk::OrientationDistributionFunction< float, 2*NumberOfSignalFeatures > odf; itk::ImageRegionIterator< typename InterpolationFilterType::OutputImageType > it(filter->GetOutput(), filter->GetOutput()->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { typename DwiFeatureImageType::PixelType pix; int f = 0; for (unsigned int i = 0; i0) // only used directions on one hemisphere { pix[f] = it.Get()[i]; f++; } } dwiFeatureImage->SetPixel(it.GetIndex(), pix); ++it; } m_DwiFeatureImages.push_back(dwiFeatureImage); return true; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InitForTracking() { MITK_INFO << "Initializing random forest tracker."; if (m_NeedsDataInit) { InputDataValidForTracking(); m_DwiFeatureImages.clear(); InitDwiImageFeatures<>(m_InputDwis.at(0)); // initialize interpolators m_DwiFeatureImageInterpolator = DwiFeatureImageInterpolatorType::New(); m_DwiFeatureImageInterpolator->SetInputImage(m_DwiFeatureImages.at(0)); m_AdditionalFeatureImageInterpolators.clear(); for (auto afi_vec : m_AdditionalFeatureImages) { std::vector< FloatImageInterpolatorType::Pointer > v; for (auto img : afi_vec) { FloatImageInterpolatorType::Pointer interp = FloatImageInterpolatorType::New(); interp->SetInputImage(img); v.push_back(interp); } m_AdditionalFeatureImageInterpolators.push_back(v); } + auto double_dir = m_DwiFeatureImages.at(0)->GetDirection().GetVnlMatrix(); + for (int r=0; r<3; r++) + for (int c=0; c<3; c++) + { + m_FloatImageRotation[r][c] = double_dir[r][c]; + } + + this->CalculateMinVoxelSize(); m_NeedsDataInit = false; } } template< int ShOrder, int NumberOfSignalFeatures > vnl_vector_fixed TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::ProposeDirection(const itk::Point& pos, std::deque >& olddirs, itk::Index<3>& oldIndex) { vnl_vector_fixed output_direction; output_direction.fill(0); itk::Index<3> idx; m_DwiFeatureImages.at(0)->TransformPhysicalPointToIndex(pos, idx); bool check_last_dir = false; vnl_vector_fixed last_dir; if (!olddirs.empty()) { last_dir = olddirs.back(); if (last_dir.magnitude()>0.5) check_last_dir = true; } - if (!m_Interpolate && oldIndex==idx) + if (!m_Parameters->m_InterpolateTractographyData && oldIndex==idx) return last_dir; // store feature pixel values in a vigra data type vigra::MultiArray<2, float> featureData = vigra::MultiArray<2, float>( vigra::Shape2(1,m_Forest->GetNumFeatures()) ); featureData.init(0.0); - typename DwiFeatureImageType::PixelType dwiFeaturePixel = mitk::imv::GetImageValue< typename DwiFeatureImageType::PixelType >(pos, m_Interpolate, m_DwiFeatureImageInterpolator); + typename DwiFeatureImageType::PixelType dwiFeaturePixel = mitk::imv::GetImageValue< typename DwiFeatureImageType::PixelType >(pos, m_Parameters->m_InterpolateTractographyData, m_DwiFeatureImageInterpolator); for (unsigned int f=0; f direction_matrix = m_DwiFeatureImages.at(0)->GetDirection().GetVnlMatrix(); vnl_matrix_fixed inverse_direction_matrix = m_DwiFeatureImages.at(0)->GetInverseDirection().GetVnlMatrix(); // append normalized previous direction(s) to feature vector int i = 0; vnl_vector_fixed ref; ref.fill(0); ref[0]=1; for (auto d : olddirs) { vnl_vector_fixed tempD; tempD[0] = d[0]; tempD[1] = d[1]; tempD[2] = d[2]; - if (m_FlipX) + if (m_Parameters->m_FlipX) tempD[0] *= -1; - if (m_FlipY) + if (m_Parameters->m_FlipY) tempD[1] *= -1; - if (m_FlipZ) + if (m_Parameters->m_FlipZ) tempD[2] *= -1; tempD = inverse_direction_matrix * tempD; last_dir[0] = tempD[0]; last_dir[1] = tempD[1]; last_dir[2] = tempD[2]; int c = 0; for (int f=NumberOfSignalFeatures+3*i; f0) { int c = 0; for (auto interpolator : m_AdditionalFeatureImageInterpolators.at(0)) { float v = mitk::imv::GetImageValue(pos, false, interpolator); - featureData(0,NumberOfSignalFeatures+m_NumPreviousDirections*3+c) = v; + featureData(0,NumberOfSignalFeatures+m_Parameters->m_NumPreviousDirections*3+c) = v; c++; } } // perform classification vigra::MultiArray<2, float> probs(vigra::Shape2(1, m_Forest->GetNumClasses())); m_Forest->PredictProbabilities(featureData, probs); vnl_vector< float > angles = m_OdfFloatDirs*last_dir; vnl_vector< float > probs2; probs2.set_size(m_DirectionContainer.size()); probs2.fill(0.0); // used for probabilistic direction sampling float probs_sum = 0; float pNonFib = 0; // probability that we left the white matter float w = 0; // weight of the predicted direction for (int i=0; iGetNumClasses(); i++) // for each class (number of possible directions + out-of-wm class) { if (probs(0,i)>0) // if probability of respective class is 0, do nothing { // get label of class (does not correspond to the loop variable i) unsigned int classLabel = m_Forest->IndexToClassLabel(i); if (classLabelm_Mode==MODE::PROBABILISTIC) { probs2[classLabel] = probs(0,i); if (check_last_dir) probs2[classLabel] *= abs_angle; probs_sum += probs2[classLabel]; } - else if (m_Mode==MODE::DETERMINISTIC) + else if (m_Parameters->m_Mode==MODE::DETERMINISTIC) { vnl_vector_fixed d = m_DirectionContainer.at(classLabel); // get direction vector assiciated with the respective direction index if (check_last_dir) // do we have a previous streamline direction or did we just start? { - if (abs_angle>=m_AngularThreshold) // is angle between the directions smaller than our hard threshold? + if (abs_angle>=m_Parameters->GetAngularThresholdDot()) // is angle between the directions smaller than our hard threshold? { if (angle<0) // make sure we don't walk backwards d *= -1; float w_i = probs(0,i)*abs_angle; output_direction += w_i*d; // weight contribution to output direction with its probability and the angular deviation from the previous direction w += w_i; // increase output weight of the final direction } } else { output_direction += probs(0,i)*d; w += probs(0,i); } } } else pNonFib += probs(0,i); // probability that we are not in the white matter anymore } } - if (m_Mode==MODE::PROBABILISTIC && pNonFib<0.5) + if (m_Parameters->m_Mode==MODE::PROBABILISTIC && pNonFib<0.5) { boost::random::discrete_distribution dist(probs2.begin(), probs2.end()); int sampled_idx = 0; for (int i=0; i<50; i++) // we allow 50 trials to exceed m_AngularThreshold { #pragma omp critical { boost::random::variate_generator> sampler(m_Rng, dist); sampled_idx = sampler(); } - if ( probs2[sampled_idx]>0.1 && (!check_last_dir || (check_last_dir && fabs(angles[sampled_idx])>=m_AngularThreshold)) ) + if ( probs2[sampled_idx]>0.1 && (!check_last_dir || (check_last_dir && fabs(angles[sampled_idx])>=m_Parameters->GetAngularThresholdDot())) ) break; } output_direction = m_DirectionContainer.at(sampled_idx); w = probs2[sampled_idx]; if (check_last_dir && angles[sampled_idx]<0) // make sure we don't walk backwards output_direction *= -1; } // if we did not find a suitable direction, make sure that we return (0,0,0) if (pNonFib>w && w>0) output_direction.fill(0.0); else { vnl_vector_fixed tempD; tempD[0] = output_direction[0]; tempD[1] = output_direction[1]; tempD[2] = output_direction[2]; tempD = direction_matrix * tempD; output_direction[0] = tempD[0]; output_direction[1] = tempD[1]; output_direction[2] = tempD[2]; - if (m_FlipX) + if (m_Parameters->m_FlipX) output_direction[0] *= -1; - if (m_FlipY) + if (m_Parameters->m_FlipY) output_direction[1] *= -1; - if (m_FlipZ) + if (m_Parameters->m_FlipZ) output_direction[2] *= -1; + if (m_Parameters->m_ApplyDirectionMatrix) + output_direction = m_FloatImageRotation*output_direction; } return output_direction * w; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::StartTraining() { m_StartTime = std::chrono::system_clock::now(); InputDataValidForTraining(); InitForTraining(); CalculateTrainingSamples(); MITK_INFO << "Maximum tree depths: " << m_MaxTreeDepth; MITK_INFO << "Sample fraction per tree: " << m_SampleFraction; MITK_INFO << "Number of trees: " << m_NumTrees; DefaultSplitType splitter; splitter.UsePointBasedWeights(true); splitter.SetWeights(m_Weights); splitter.UseRandomSplit(false); splitter.SetPrecision(mitk::eps); splitter.SetMaximumTreeDepth(m_MaxTreeDepth); std::vector< std::shared_ptr< vigra::RandomForest > > trees; int count = 0; #pragma omp parallel for for (int i = 0; i < m_NumTrees; ++i) { std::shared_ptr< vigra::RandomForest > lrf = std::make_shared< vigra::RandomForest >(); lrf->set_options().use_stratification(vigra::RF_NONE); // How the data should be made equal lrf->set_options().sample_with_replacement(true); // if sampled with replacement or not lrf->set_options().samples_per_tree(m_SampleFraction); // Fraction of samples that are used to train a tree lrf->set_options().tree_count(1); // Number of trees that are calculated; lrf->set_options().min_split_node_size(5); // Minimum number of datapoints that must be in a node lrf->ext_param_.max_tree_depth = m_MaxTreeDepth; lrf->learn(m_FeatureData, m_LabelData,vigra::rf::visitors::VisitorBase(),splitter); #pragma omp critical { count++; MITK_INFO << "Tree " << count << " finished training."; trees.push_back(lrf); } } for (int i = 1; i < m_NumTrees; ++i) trees.at(0)->trees_.push_back(trees.at(i)->trees_[0]); std::shared_ptr< vigra::RandomForest > forest = trees.at(0); forest->options_.tree_count_ = m_NumTrees; m_Forest = mitk::TractographyForest::New(forest); MITK_INFO << "Training finsihed"; m_EndTime = std::chrono::system_clock::now(); std::chrono::hours hh = std::chrono::duration_cast(m_EndTime - m_StartTime); std::chrono::minutes mm = std::chrono::duration_cast(m_EndTime - m_StartTime); mm %= 60; MITK_INFO << "Training took " << hh.count() << "h and " << mm.count() << "m"; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InputDataValidForTraining() { if (m_InputDwis.empty()) mitkThrow() << "No diffusion-weighted images set!"; if (m_Tractograms.empty()) mitkThrow() << "No tractograms set!"; if (m_InputDwis.size()!=m_Tractograms.size()) mitkThrow() << "Unequal number of diffusion-weighted images and tractograms detected!"; } template< int ShOrder, int NumberOfSignalFeatures > bool TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::IsForestValid() { int additional_features = 0; if (m_AdditionalFeatureImages.size()>0) additional_features = m_AdditionalFeatureImages.at(0).size(); if (!m_Forest) MITK_INFO << "No forest available!"; else { if (m_Forest->GetNumTrees() <= 0) MITK_ERROR << "Forest contains no trees!"; - if ( m_Forest->GetNumFeatures() != static_cast(NumberOfSignalFeatures+3*m_NumPreviousDirections+additional_features) ) - MITK_ERROR << "Wrong number of features in forest: got " << m_Forest->GetNumFeatures() << ", expected " << (NumberOfSignalFeatures+3*m_NumPreviousDirections+additional_features); + if ( m_Forest->GetNumFeatures() != static_cast(NumberOfSignalFeatures+3*m_Parameters->m_NumPreviousDirections+additional_features) ) + MITK_ERROR << "Wrong number of features in forest: got " << m_Forest->GetNumFeatures() << ", expected " << (NumberOfSignalFeatures+3*m_Parameters->m_NumPreviousDirections+additional_features); } - if(m_Forest && m_Forest->GetNumTrees()>0 && m_Forest->GetNumFeatures() == static_cast(NumberOfSignalFeatures+3*m_NumPreviousDirections+additional_features)) + if(m_Forest && m_Forest->GetNumTrees()>0 && m_Forest->GetNumFeatures() == static_cast(NumberOfSignalFeatures+3*m_Parameters->m_NumPreviousDirections+additional_features)) return true; return false; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::InitForTraining() { MITK_INFO << "Spherical signal interpolation and sampling ..."; for (unsigned int i=0; i(m_InputDwis.at(i)); if (i>=m_AdditionalFeatureImages.size()) { m_AdditionalFeatureImages.push_back(std::vector< ItkFloatImgType::Pointer >()); } if (i>=m_FiberVolumeModImages.size()) { ItkFloatImgType::Pointer img = ItkFloatImgType::New(); img->SetSpacing( m_DwiFeatureImages.at(i)->GetSpacing() ); img->SetOrigin( m_DwiFeatureImages.at(i)->GetOrigin() ); img->SetDirection( m_DwiFeatureImages.at(i)->GetDirection() ); img->SetLargestPossibleRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); img->SetBufferedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); img->SetRequestedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); img->Allocate(); img->FillBuffer(1); m_FiberVolumeModImages.push_back(img); } if (m_FiberVolumeModImages.at(i)==nullptr) { m_FiberVolumeModImages.at(i) = ItkFloatImgType::New(); m_FiberVolumeModImages.at(i)->SetSpacing( m_DwiFeatureImages.at(i)->GetSpacing() ); m_FiberVolumeModImages.at(i)->SetOrigin( m_DwiFeatureImages.at(i)->GetOrigin() ); m_FiberVolumeModImages.at(i)->SetDirection( m_DwiFeatureImages.at(i)->GetDirection() ); m_FiberVolumeModImages.at(i)->SetLargestPossibleRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_FiberVolumeModImages.at(i)->SetBufferedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_FiberVolumeModImages.at(i)->SetRequestedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_FiberVolumeModImages.at(i)->Allocate(); m_FiberVolumeModImages.at(i)->FillBuffer(1); } if (i>=m_MaskImages.size()) { ItkUcharImgType::Pointer newMask = ItkUcharImgType::New(); newMask->SetSpacing( m_DwiFeatureImages.at(i)->GetSpacing() ); newMask->SetOrigin( m_DwiFeatureImages.at(i)->GetOrigin() ); newMask->SetDirection( m_DwiFeatureImages.at(i)->GetDirection() ); newMask->SetLargestPossibleRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); newMask->SetBufferedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); newMask->SetRequestedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); newMask->Allocate(); newMask->FillBuffer(1); m_MaskImages.push_back(newMask); } if (m_MaskImages.at(i)==nullptr) { m_MaskImages.at(i) = ItkUcharImgType::New(); m_MaskImages.at(i)->SetSpacing( m_DwiFeatureImages.at(i)->GetSpacing() ); m_MaskImages.at(i)->SetOrigin( m_DwiFeatureImages.at(i)->GetOrigin() ); m_MaskImages.at(i)->SetDirection( m_DwiFeatureImages.at(i)->GetDirection() ); m_MaskImages.at(i)->SetLargestPossibleRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_MaskImages.at(i)->SetBufferedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_MaskImages.at(i)->SetRequestedRegion( m_DwiFeatureImages.at(i)->GetLargestPossibleRegion() ); m_MaskImages.at(i)->Allocate(); m_MaskImages.at(i)->FillBuffer(1); } } // initialize interpolators m_AdditionalFeatureImageInterpolators.clear(); for (auto afi_vec : m_AdditionalFeatureImages) { std::vector< FloatImageInterpolatorType::Pointer > v; for (auto img : afi_vec) { FloatImageInterpolatorType::Pointer interp = FloatImageInterpolatorType::New(); interp->SetInputImage(img); v.push_back(interp); } m_AdditionalFeatureImageInterpolators.push_back(v); } MITK_INFO << "Resampling fibers and calculating number of samples ..."; m_NumberOfSamples = 0; m_SampleUsage.clear(); for (unsigned int t=0; t::Pointer env = itk::TractDensityImageFilter< ItkUcharImgType >::New(); env->SetFiberBundle(m_Tractograms.at(t)); env->SetInputImage(mask); env->SetBinaryOutput(true); env->SetUseImageGeometry(true); env->Update(); wmmask = env->GetOutput(); if (t>=m_WhiteMatterImages.size()) m_WhiteMatterImages.push_back(wmmask); else m_WhiteMatterImages.at(t) = wmmask; } // Calculate white-matter samples if (m_WmSampleDistance<0) { typename DwiFeatureImageType::Pointer image = m_DwiFeatureImages.at(t); float minSpacing = 1; if(image->GetSpacing()[0]GetSpacing()[1] && image->GetSpacing()[0]GetSpacing()[2]) minSpacing = image->GetSpacing()[0]; else if (image->GetSpacing()[1] < image->GetSpacing()[2]) minSpacing = image->GetSpacing()[1]; else minSpacing = image->GetSpacing()[2]; m_WmSampleDistance = minSpacing*0.5; } m_Tractograms.at(t)->ResampleLinear(m_WmSampleDistance); int wmSamples = m_Tractograms.at(t)->GetNumberOfPoints()-2*m_Tractograms.at(t)->GetNumFibers(); if (m_BidirectionalFiberSampling) wmSamples *= 2; if (m_ZeroDirWmFeatures) - wmSamples *= (m_NumPreviousDirections+1); + wmSamples *= (m_Parameters->m_NumPreviousDirections+1); MITK_INFO << "White matter samples available: " << wmSamples; // upper limit for samples if (m_MaxNumWmSamples>0 && wmSamples>m_MaxNumWmSamples) { if ((float)m_MaxNumWmSamples/wmSamples > 0.8) { m_SampleUsage.push_back(std::vector(wmSamples, true)); m_NumberOfSamples += wmSamples; } else { m_SampleUsage.push_back(std::vector(wmSamples, false)); m_NumberOfSamples += m_MaxNumWmSamples; wmSamples = m_MaxNumWmSamples; MITK_INFO << "Limiting white matter samples to: " << m_MaxNumWmSamples; itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randgen = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); randgen->SetSeed(); int c = 0; while (cGetIntegerVariate(m_MaxNumWmSamples-1); if (m_SampleUsage[t][idx]==false) { m_SampleUsage[t][idx]=true; ++c; } } } } else { m_SampleUsage.push_back(std::vector(wmSamples, true)); m_NumberOfSamples += wmSamples; } // calculate gray-matter samples itk::ImageRegionConstIterator it(wmmask, wmmask->GetLargestPossibleRegion()); int OUTOFWM = 0; while(!it.IsAtEnd()) { if (it.Get()==0 && mask->GetPixel(it.GetIndex())>0) OUTOFWM++; ++it; } MITK_INFO << "Non-white matter voxels: " << OUTOFWM; if (m_GmSamplesPerVoxel>0) { m_GmSamples.push_back(m_GmSamplesPerVoxel); m_NumberOfSamples += m_GmSamplesPerVoxel*OUTOFWM; } else if (OUTOFWM>0) { int gm_per_voxel = 0.5+(float)wmSamples/(float)OUTOFWM; if (gm_per_voxel<=0) gm_per_voxel = 1; m_GmSamples.push_back(gm_per_voxel); m_NumberOfSamples += m_GmSamples.back()*OUTOFWM; MITK_INFO << "Non-white matter samples per voxel: " << m_GmSamples.back(); } else { m_GmSamples.push_back(0); } MITK_INFO << "Non-white matter samples: " << m_GmSamples.back()*OUTOFWM; } MITK_INFO << "Number of samples: " << m_NumberOfSamples; } template< int ShOrder, int NumberOfSignalFeatures > void TrackingHandlerRandomForest< ShOrder, NumberOfSignalFeatures >::CalculateTrainingSamples() { vnl_vector_fixed ref; ref.fill(0); ref[0]=1; - m_FeatureData.reshape( vigra::Shape2(m_NumberOfSamples, NumberOfSignalFeatures+m_NumPreviousDirections*3+m_AdditionalFeatureImages.at(0).size()) ); + m_FeatureData.reshape( vigra::Shape2(m_NumberOfSamples, NumberOfSignalFeatures+m_Parameters->m_NumPreviousDirections*3+m_AdditionalFeatureImages.at(0).size()) ); m_LabelData.reshape( vigra::Shape2(m_NumberOfSamples,1) ); m_Weights.reshape( vigra::Shape2(m_NumberOfSamples,1) ); MITK_INFO << "Number of features: " << m_FeatureData.shape(1); itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer m_RandGen = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); m_RandGen->SetSeed(); MITK_INFO << "Creating training data ..."; unsigned int sampleCounter = 0; for (unsigned int t=0; tSetInputImage(fiber_folume); typename DwiFeatureImageType::Pointer image = m_DwiFeatureImages.at(t); typename DwiFeatureImageInterpolatorType::Pointer dwi_interp = DwiFeatureImageInterpolatorType::New(); dwi_interp->SetInputImage(image); ItkUcharImgType::Pointer wmMask = m_WhiteMatterImages.at(t); ItkUcharImgType::Pointer mask; if (t it(wmMask, wmMask->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { if (it.Get()==0 && (mask.IsNull() || (mask.IsNotNull() && mask->GetPixel(it.GetIndex())>0))) { typename DwiFeatureImageType::PixelType pix = image->GetPixel(it.GetIndex()); // random directions for (unsigned int i=0; iGetIntegerVariate(m_NumPreviousDirections); // how many directions should be zero? - for (unsigned int i=0; iGetIntegerVariate(m_Parameters->m_NumPreviousDirections); // how many directions should be zero? + for (unsigned int i=0; im_NumPreviousDirections; i++) { int c=0; vnl_vector_fixed probe; if (static_cast(i)GetVariate()*2-1; probe[1] = m_RandGen->GetVariate()*2-1; probe[2] = m_RandGen->GetVariate()*2-1; probe.normalize(); if (dot_product(ref, probe)<0) probe *= -1; } for (unsigned int f=NumberOfSignalFeatures+3*i; f itkP; image->TransformIndexToPhysicalPoint(it.GetIndex(), itkP); float v = mitk::imv::GetImageValue(itkP, false, interpolator); - m_FeatureData(sampleCounter,NumberOfSignalFeatures+m_NumPreviousDirections*3+add_feat_c) = v; + m_FeatureData(sampleCounter,NumberOfSignalFeatures+m_Parameters->m_NumPreviousDirections*3+add_feat_c) = v; add_feat_c++; } // label and sample weight m_LabelData(sampleCounter,0) = m_DirectionContainer.size(); m_Weights(sampleCounter,0) = 1.0; sampleCounter++; } } ++it; } unsigned int num_gm_samples = sampleCounter; // white matter samples mitk::FiberBundle::Pointer fib = m_Tractograms.at(t); vtkSmartPointer< vtkPolyData > polyData = fib->GetFiberPolyData(); vnl_vector_fixed zero_dir; zero_dir.fill(0.0); for (unsigned int i=0; iGetNumFibers(); i++) { vtkCell* cell = polyData->GetCell(i); int numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); float fiber_weight = fib->GetFiberWeight(i); - for (int n = 0; n <= static_cast(m_NumPreviousDirections); ++n) + for (int n = 0; n <= static_cast(m_Parameters->m_NumPreviousDirections); ++n) { if (!m_ZeroDirWmFeatures) - n = m_NumPreviousDirections; + n = m_Parameters->m_NumPreviousDirections; for (bool reverse : {false, true}) { for (int j=1; j itkP1, itkP2; - int num_nonzero_dirs = m_NumPreviousDirections; + int num_nonzero_dirs = m_Parameters->m_NumPreviousDirections; if (!reverse) num_nonzero_dirs = std::min(n, j); else num_nonzero_dirs = std::min(n, numPoints-j-1); vnl_vector_fixed dir; // zero directions - for (unsigned int k=0; km_NumPreviousDirections-num_nonzero_dirs; k++) { dir.fill(0.0); int c = 0; for (unsigned int f=NumberOfSignalFeatures+3*k; fGetPoint(j-n_idx); itkP1[0] = p[0]; itkP1[1] = p[1]; itkP1[2] = p[2]; p = points->GetPoint(j-n_idx+1); itkP2[0] = p[0]; itkP2[1] = p[1]; itkP2[2] = p[2]; } else { p = points->GetPoint(j+n_idx); itkP1[0] = p[0]; itkP1[1] = p[1]; itkP1[2] = p[2]; p = points->GetPoint(j+n_idx-1); itkP2[0] = p[0]; itkP2[1] = p[1]; itkP2[2] = p[2]; } dir[0]=itkP1[0]-itkP2[0]; dir[1]=itkP1[1]-itkP2[1]; dir[2]=itkP1[2]-itkP2[2]; if (dir.magnitude()<0.0001) mitkThrow() << "streamline error!"; dir.normalize(); if (dir[0]!=dir[0] || dir[1]!=dir[1] || dir[2]!=dir[2]) mitkThrow() << "ERROR: NaN direction!"; if (dot_product(ref, dir)<0) dir *= -1; int c = 0; - for (unsigned int f=NumberOfSignalFeatures+3*(k+m_NumPreviousDirections-num_nonzero_dirs); fm_NumPreviousDirections-num_nonzero_dirs); fm_NumPreviousDirections-num_nonzero_dirs); f++) { m_FeatureData(sampleCounter,f) = dir[c]; c++; } } // get target direction double* p = points->GetPoint(j); itkP1[0] = p[0]; itkP1[1] = p[1]; itkP1[2] = p[2]; if (reverse) { p = points->GetPoint(j-1); itkP2[0] = p[0]; itkP2[1] = p[1]; itkP2[2] = p[2]; } else { p = points->GetPoint(j+1); itkP2[0] = p[0]; itkP2[1] = p[1]; itkP2[2] = p[2]; } dir[0]=itkP2[0]-itkP1[0]; dir[1]=itkP2[1]-itkP1[1]; dir[2]=itkP2[2]-itkP1[2]; if (dir.magnitude()<0.0001) mitkThrow() << "streamline error!"; dir.normalize(); if (dir[0]!=dir[0] || dir[1]!=dir[1] || dir[2]!=dir[2]) mitkThrow() << "ERROR: NaN direction!"; if (dot_product(ref, dir)<0) dir *= -1; // image features float volume_mod = mitk::imv::GetImageValue(itkP1, false, volume_interpolator); // diffusion signal features - typename DwiFeatureImageType::PixelType pix = mitk::imv::GetImageValue< typename DwiFeatureImageType::PixelType >(itkP1, m_Interpolate, dwi_interp); + typename DwiFeatureImageType::PixelType pix = mitk::imv::GetImageValue< typename DwiFeatureImageType::PixelType >(itkP1, m_Parameters->m_InterpolateTractographyData, dwi_interp); for (unsigned int f=0; f(itkP1, false, interpolator); add_feat_c++; m_FeatureData(sampleCounter,NumberOfSignalFeatures+2+add_feat_c) = v; } // set label values float angle = 0; float m = dir.magnitude(); if (m>0.0001) { int l = 0; for (auto d : m_DirectionContainer) { float a = fabs(dot_product(dir, d)); if (a>angle) { m_LabelData(sampleCounter,0) = l; m_Weights(sampleCounter,0) = fiber_weight*volume_mod; angle = a; } l++; } } sampleCounter++; } if (!m_BidirectionalFiberSampling) // don't sample fibers backward break; } } } } m_Tractograms.clear(); MITK_INFO << "done"; } } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.h b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.h index 5b911770d4..136c6be6f0 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.h @@ -1,166 +1,163 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _TrackingForestHandler #define _TrackingForestHandler #include #include #include #include #include #include #include #include #undef DIFFERENCE #define VIGRA_STATIC_LIB #include #include #include #include #include #include #define _USE_MATH_DEFINES #include namespace mitk { /** * \brief Manages random forests for fiber tractography. The preparation of the features from the inputa data and the training process are handled here. The data preprocessing and actual prediction for the tracking process is also performed here. The tracking itself is performed in MLBSTrackingFilter. */ template< int ShOrder, int NumberOfSignalFeatures > class TrackingHandlerRandomForest : public TrackingDataHandler { public: TrackingHandlerRandomForest(); ~TrackingHandlerRandomForest() override; typedef itk::Image< itk::Vector< float, NumberOfSignalFeatures > , 3 > DwiFeatureImageType; typedef itk::LinearInterpolateImageFunction< DwiFeatureImageType, float > DwiFeatureImageInterpolatorType; typedef itk::LinearInterpolateImageFunction< ItkFloatImgType, float > FloatImageInterpolatorType; typedef mitk::ThresholdSplit >,int,vigra::ClassificationTag> DefaultSplitType; void SetDwis( std::vector< Image::Pointer > images ){ m_InputDwis = images; DataModified(); } void AddDwi( Image::Pointer img ){ m_InputDwis.push_back(img); DataModified(); } void SetTractograms( std::vector< FiberBundle::Pointer > tractograms ) { m_Tractograms.clear(); for (auto fib : tractograms) m_Tractograms.push_back(fib); DataModified(); } void SetMaskImages( std::vector< ItkUcharImgType::Pointer > images ){ m_MaskImages = images; DataModified(); } void SetWhiteMatterImages( std::vector< ItkUcharImgType::Pointer > images ){ m_WhiteMatterImages = images; DataModified(); } void SetFiberVolumeModImages( std::vector< ItkFloatImgType::Pointer > images ){ m_FiberVolumeModImages = images; DataModified(); } void SetAdditionalFeatureImages( std::vector< std::vector< ItkFloatImgType::Pointer > > images ){ m_AdditionalFeatureImages = images; DataModified(); } void StartTraining(); - void SetMode( MODE m ) override{ m_Mode = m; } void SetForest(mitk::TractographyForest::Pointer forest){ m_Forest = forest; } void SetMaxNumWmSamples(int num){ m_MaxNumWmSamples=num; } - void SetNumPreviousDirections( unsigned int num ){ m_NumPreviousDirections=num; } void SetNumTrees(int num){ m_NumTrees = num; } void SetMaxTreeDepth(int depth){ m_MaxTreeDepth = depth; } void SetFiberSamplingStep(float step){ m_WmSampleDistance = step; } void SetGrayMatterSamplesPerVoxel(int samples){ m_GmSamplesPerVoxel = samples; } void SetSampleFraction(float fraction){ m_SampleFraction = fraction; } void SetBidirectionalFiberSampling(bool val) { m_BidirectionalFiberSampling = val; } void SetZeroDirWmFeatures(bool val) { m_ZeroDirWmFeatures = val; } void InitForTracking() override; ///< calls InputDataValidForTracking() and creates feature images vnl_vector_fixed ProposeDirection(const itk::Point& pos, std::deque< vnl_vector_fixed >& olddirs, itk::Index<3>& oldIndex) override; ///< predicts next progression direction at the given position bool WorldToIndex(itk::Point& pos, itk::Index<3>& index) override; bool IsForestValid(); ///< true is forest is not null, has more than 0 trees and the correct number of features (NumberOfSignalFeatures + 3) mitk::TractographyForest::Pointer GetForest(){ return m_Forest; } ItkUcharImgType::SpacingType GetSpacing() override{ return m_DwiFeatureImages.at(0)->GetSpacing(); } itk::Point GetOrigin() override{ return m_DwiFeatureImages.at(0)->GetOrigin(); } ItkUcharImgType::DirectionType GetDirection() override{ return m_DwiFeatureImages.at(0)->GetDirection(); } ItkUcharImgType::RegionType GetLargestPossibleRegion() override{ return m_DwiFeatureImages.at(0)->GetLargestPossibleRegion(); } protected: void InputDataValidForTracking(); ///< check if raw data is set and tracking forest is valid template typename std::enable_if::type InitDwiImageFeatures(mitk::Image::Pointer mitk_dwi); template typename std::enable_if= 100, T>::type InitDwiImageFeatures(mitk::Image::Pointer mitk_dwi); void InputDataValidForTraining(); ///< Check if everything is tehere for training (raw datasets, fiber tracts) void InitForTraining(); ///< Generate masks if necessary, resample fibers, spherically interpolate raw DWIs void CalculateTrainingSamples(); ///< Calculate GM and WM features using the interpolated raw data, the WM masks and the fibers std::vector< Image::Pointer > m_InputDwis; ///< original input DWI data mitk::TractographyForest::Pointer m_Forest; ///< random forest classifier std::chrono::time_point m_StartTime; std::chrono::time_point m_EndTime; std::vector< typename DwiFeatureImageType::Pointer > m_DwiFeatureImages; std::vector< std::vector< ItkFloatImgType::Pointer > > m_AdditionalFeatureImages; std::vector< ItkFloatImgType::Pointer > m_FiberVolumeModImages; ///< used to correct the fiber density std::vector< FiberBundle::Pointer > m_Tractograms; ///< training tractograms std::vector< ItkUcharImgType::Pointer > m_MaskImages; ///< binary mask images to constrain training to a certain area (e.g. brain mask) std::vector< ItkUcharImgType::Pointer > m_WhiteMatterImages; ///< defines white matter voxels. if not set, theses mask images are automatically generated from the input tractograms float m_WmSampleDistance; ///< deterines the number of white matter samples (distance of sampling points on each fiber). int m_NumTrees; ///< number of trees in random forest int m_MaxTreeDepth; ///< limits the tree depth float m_SampleFraction; ///< fraction of samples used to train each tree unsigned int m_NumberOfSamples; ///< stores overall number of samples used for training std::vector< unsigned int > m_GmSamples; ///< number of gray matter samples int m_GmSamplesPerVoxel; ///< number of gray matter samplees per voxel. if -1, then the number is automatically chosen to gain an overall number of GM samples close to the number of WM samples. vigra::MultiArray<2, float> m_FeatureData; ///< vigra container for training features - unsigned int m_NumPreviousDirections; ///< How many "old" directions should be used as classification features? // only for tracking vigra::MultiArray<2, float> m_LabelData; ///< vigra container for training labels vigra::MultiArray<2, double> m_Weights; ///< vigra container for training sample weights std::vector< vnl_vector_fixed > m_DirectionContainer; vnl_matrix< float > m_OdfFloatDirs; bool m_BidirectionalFiberSampling; bool m_ZeroDirWmFeatures; int m_MaxNumWmSamples; std::vector< std::vector< bool > > m_SampleUsage; vnl_matrix_fixed m_ImageDirection; vnl_matrix_fixed m_ImageDirectionInverse; typename DwiFeatureImageInterpolatorType::Pointer m_DwiFeatureImageInterpolator; std::vector< std::vector< FloatImageInterpolatorType::Pointer > > m_AdditionalFeatureImageInterpolators; }; } #include "mitkTrackingHandlerRandomForest.cpp" #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.cpp index 0581e41ec7..b68d185e93 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.cpp @@ -1,371 +1,378 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTrackingHandlerTensor.h" namespace mitk { TrackingHandlerTensor::TrackingHandlerTensor() - : m_FaThreshold(0.1) - , m_F(1.0) - , m_G(0.0) - , m_InterpolateTensors(true) + : m_InterpolateTensors(true) , m_NumberOfInputs(0) { m_FaInterpolator = itk::LinearInterpolateImageFunction< itk::Image< float, 3 >, float >::New(); } TrackingHandlerTensor::~TrackingHandlerTensor() { } void TrackingHandlerTensor::InitForTracking() { MITK_INFO << "Initializing tensor tracker."; if (m_NeedsDataInit) { m_NumberOfInputs = m_TensorImages.size(); for (int i=0; iSetSpacing( m_TensorImages.at(0)->GetSpacing() ); pdImage->SetOrigin( m_TensorImages.at(0)->GetOrigin() ); pdImage->SetDirection( m_TensorImages.at(0)->GetDirection() ); pdImage->SetRegions( m_TensorImages.at(0)->GetLargestPossibleRegion() ); pdImage->Allocate(); m_PdImage.push_back(pdImage); ItkDoubleImgType::Pointer emaxImage = ItkDoubleImgType::New(); emaxImage->SetSpacing( m_TensorImages.at(0)->GetSpacing() ); emaxImage->SetOrigin( m_TensorImages.at(0)->GetOrigin() ); emaxImage->SetDirection( m_TensorImages.at(0)->GetDirection() ); emaxImage->SetRegions( m_TensorImages.at(0)->GetLargestPossibleRegion() ); emaxImage->Allocate(); emaxImage->FillBuffer(1.0); m_EmaxImage.push_back(emaxImage); } bool useUserFaImage = true; if (m_FaImage.IsNull()) { m_FaImage = ItkFloatImgType::New(); m_FaImage->SetSpacing( m_TensorImages.at(0)->GetSpacing() ); m_FaImage->SetOrigin( m_TensorImages.at(0)->GetOrigin() ); m_FaImage->SetDirection( m_TensorImages.at(0)->GetDirection() ); m_FaImage->SetRegions( m_TensorImages.at(0)->GetLargestPossibleRegion() ); m_FaImage->Allocate(); m_FaImage->FillBuffer(0.0); useUserFaImage = false; } typedef itk::DiffusionTensor3D TensorType; for (int x=0; x<(int)m_TensorImages.at(0)->GetLargestPossibleRegion().GetSize()[0]; x++) for (int y=0; y<(int)m_TensorImages.at(0)->GetLargestPossibleRegion().GetSize()[1]; y++) for (int z=0; z<(int)m_TensorImages.at(0)->GetLargestPossibleRegion().GetSize()[2]; z++) { ItkTensorImageType::IndexType index; index[0] = x; index[1] = y; index[2] = z; for (int i=0; i dir; tensor = m_TensorImages.at(i)->GetPixel(index); tensor.ComputeEigenAnalysis(eigenvalues, eigenvectors); dir[0] = eigenvectors(2, 0); dir[1] = eigenvectors(2, 1); dir[2] = eigenvectors(2, 2); if (dir.magnitude()>mitk::eps) dir.normalize(); else dir.fill(0.0); m_PdImage.at(i)->SetPixel(index, dir); if (!useUserFaImage) m_FaImage->SetPixel(index, m_FaImage->GetPixel(index)+tensor.GetFractionalAnisotropy()); m_EmaxImage.at(i)->SetPixel(index, 2/eigenvalues[2]); } if (!useUserFaImage) m_FaImage->SetPixel(index, m_FaImage->GetPixel(index)/m_NumberOfInputs); } + auto double_dir = m_TensorImages.at(0)->GetDirection().GetVnlMatrix(); + for (int r=0; r<3; r++) + for (int c=0; c<3; c++) + { + m_FloatImageRotation[r][c] = double_dir[r][c]; + } + + this->CalculateMinVoxelSize(); m_NeedsDataInit = false; } - if (m_F+m_G>1.0) + if (m_Parameters->m_F+m_Parameters->m_G>1.0) { - float temp = m_F+m_G; - m_F /= temp; - m_G /= temp; + float temp = m_Parameters->m_F+m_Parameters->m_G; + m_Parameters->m_F /= temp; + m_Parameters->m_G /= temp; } m_FaInterpolator->SetInputImage(m_FaImage); - std::cout << "TrackingHandlerTensor - FA threshold: " << m_FaThreshold << std::endl; - std::cout << "TrackingHandlerTensor - f: " << m_F << std::endl; - std::cout << "TrackingHandlerTensor - g: " << m_G << std::endl; + std::cout << "TrackingHandlerTensor - FA threshold: " << m_Parameters->m_Cutoff << std::endl; + std::cout << "TrackingHandlerTensor - f: " << m_Parameters->m_F << std::endl; + std::cout << "TrackingHandlerTensor - g: " << m_Parameters->m_G << std::endl; } vnl_vector_fixed TrackingHandlerTensor::GetMatchingDirection(itk::Index<3> idx, vnl_vector_fixed& oldDir, int& image_num) { vnl_vector_fixed out_dir; out_dir.fill(0); float angle = 0; float mag = oldDir.magnitude(); if (magGetPixel(idx); if (out_dir.magnitude()>0.5) { image_num = i; oldDir[0] = out_dir[0]; oldDir[1] = out_dir[1]; oldDir[2] = out_dir[2]; break; } } } else { for (unsigned int i=0; i dir = m_PdImage.at(i)->GetPixel(idx); float a = dot_product(dir, oldDir); if (fabs(a)>angle) { image_num = i; angle = fabs(a); if (a<0) out_dir = -dir; else out_dir = dir; out_dir *= angle; // shrink contribution of direction if is less parallel to previous direction } } } return out_dir; } bool TrackingHandlerTensor::WorldToIndex(itk::Point& pos, itk::Index<3>& index) { m_TensorImages.at(0)->TransformPhysicalPointToIndex(pos, index); return m_TensorImages.at(0)->GetLargestPossibleRegion().IsInside(index); } vnl_vector_fixed TrackingHandlerTensor::GetDirection(itk::Point itkP, vnl_vector_fixed oldDir, TensorType& tensor) { // transform physical point to index coordinates itk::Index<3> idx; itk::ContinuousIndex< float, 3> cIdx; m_FaImage->TransformPhysicalPointToIndex(itkP, idx); m_FaImage->TransformPhysicalPointToContinuousIndex(itkP, cIdx); vnl_vector_fixed dir; dir.fill(0.0); if ( !m_FaImage->GetLargestPossibleRegion().IsInside(idx) ) return dir; int image_num = -1; - if (!m_Interpolate) + if (!m_Parameters->m_InterpolateTractographyData) { dir = GetMatchingDirection(idx, oldDir, image_num); if (image_num>=0) tensor = m_TensorImages[image_num]->GetPixel(idx) * m_EmaxImage[image_num]->GetPixel(idx); } else { float frac_x = cIdx[0] - idx[0]; float frac_y = cIdx[1] - idx[1]; float frac_z = cIdx[2] - idx[2]; if (frac_x<0) { idx[0] -= 1; frac_x += 1; } if (frac_y<0) { idx[1] -= 1; frac_y += 1; } if (frac_z<0) { idx[2] -= 1; frac_z += 1; } frac_x = 1-frac_x; frac_y = 1-frac_y; frac_z = 1-frac_z; // int coordinates inside image? if (idx[0] >= 0 && idx[0] < static_cast(m_FaImage->GetLargestPossibleRegion().GetSize(0) - 1) && idx[1] >= 0 && idx[1] < static_cast(m_FaImage->GetLargestPossibleRegion().GetSize(1) - 1) && idx[2] >= 0 && idx[2] < static_cast(m_FaImage->GetLargestPossibleRegion().GetSize(2) - 1)) { // trilinear interpolation vnl_vector_fixed interpWeights; interpWeights[0] = ( frac_x)*( frac_y)*( frac_z); interpWeights[1] = (1-frac_x)*( frac_y)*( frac_z); interpWeights[2] = ( frac_x)*(1-frac_y)*( frac_z); interpWeights[3] = ( frac_x)*( frac_y)*(1-frac_z); interpWeights[4] = (1-frac_x)*(1-frac_y)*( frac_z); interpWeights[5] = ( frac_x)*(1-frac_y)*(1-frac_z); interpWeights[6] = (1-frac_x)*( frac_y)*(1-frac_z); interpWeights[7] = (1-frac_x)*(1-frac_y)*(1-frac_z); dir = GetMatchingDirection(idx, oldDir, image_num) * interpWeights[0]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(idx) * m_EmaxImage[image_num]->GetPixel(idx) * interpWeights[0]; itk::Index<3> tmpIdx = idx; tmpIdx[0]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[1]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[1]; tmpIdx = idx; tmpIdx[1]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[2]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[2]; tmpIdx = idx; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[3]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[3]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[4]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[4]; tmpIdx = idx; tmpIdx[1]++; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[5]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[5]; tmpIdx = idx; tmpIdx[2]++; tmpIdx[0]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[6]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[6]; tmpIdx = idx; tmpIdx[0]++; tmpIdx[1]++; tmpIdx[2]++; dir += GetMatchingDirection(tmpIdx, oldDir, image_num) * interpWeights[7]; if (image_num>=0) tensor += m_TensorImages[image_num]->GetPixel(tmpIdx) * m_EmaxImage[image_num]->GetPixel(tmpIdx) * interpWeights[7]; } } return dir; } vnl_vector_fixed TrackingHandlerTensor::GetLargestEigenvector(TensorType& tensor) { vnl_vector_fixed dir; TensorType::EigenValuesArrayType eigenvalues; TensorType::EigenVectorsMatrixType eigenvectors; tensor.ComputeEigenAnalysis(eigenvalues, eigenvectors); dir[0] = eigenvectors(2, 0); dir[1] = eigenvectors(2, 1); dir[2] = eigenvectors(2, 2); if (dir.magnitude() TrackingHandlerTensor::ProposeDirection(const itk::Point& pos, std::deque >& olddirs, itk::Index<3>& oldIndex) { vnl_vector_fixed output_direction; output_direction.fill(0); TensorType tensor; tensor.Fill(0); try { itk::Index<3> index; m_TensorImages.at(0)->TransformPhysicalPointToIndex(pos, index); - float fa = mitk::imv::GetImageValue(pos, m_Interpolate, m_FaInterpolator); - if (fa(pos, m_Parameters->m_InterpolateTractographyData, m_FaInterpolator); + if (fam_Cutoff) return output_direction; vnl_vector_fixed oldDir; oldDir.fill(0.0); if (!olddirs.empty()) oldDir = olddirs.back(); - if (m_FlipX) + if (m_Parameters->m_FlipX) oldDir[0] *= -1; - if (m_FlipY) + if (m_Parameters->m_FlipY) oldDir[1] *= -1; - if (m_FlipZ) + if (m_Parameters->m_FlipZ) oldDir[2] *= -1; float old_mag = oldDir.magnitude(); - if (!m_Interpolate && oldIndex==index) + if (!m_Parameters->m_InterpolateTractographyData && oldIndex==index) return oldDir; output_direction = GetDirection(pos, oldDir, tensor); float mag = output_direction.magnitude(); if (mag>=mitk::eps) { output_direction.normalize(); - if (old_mag>0.5 && m_G>mitk::eps) // TEND tracking + if (old_mag>0.5 && m_Parameters->m_G>mitk::eps) // TEND tracking { - output_direction[0] = m_F*output_direction[0] + (1-m_F)*( (1-m_G)*oldDir[0] + m_G*(tensor[0]*oldDir[0] + tensor[1]*oldDir[1] + tensor[2]*oldDir[2])); - output_direction[1] = m_F*output_direction[1] + (1-m_F)*( (1-m_G)*oldDir[1] + m_G*(tensor[1]*oldDir[0] + tensor[3]*oldDir[1] + tensor[4]*oldDir[2])); - output_direction[2] = m_F*output_direction[2] + (1-m_F)*( (1-m_G)*oldDir[2] + m_G*(tensor[2]*oldDir[0] + tensor[4]*oldDir[1] + tensor[5]*oldDir[2])); + output_direction[0] = m_Parameters->m_F*output_direction[0] + (1-m_Parameters->m_F)*( (1-m_Parameters->m_G)*oldDir[0] + m_Parameters->m_G*(tensor[0]*oldDir[0] + tensor[1]*oldDir[1] + tensor[2]*oldDir[2])); + output_direction[1] = m_Parameters->m_F*output_direction[1] + (1-m_Parameters->m_F)*( (1-m_Parameters->m_G)*oldDir[1] + m_Parameters->m_G*(tensor[1]*oldDir[0] + tensor[3]*oldDir[1] + tensor[4]*oldDir[2])); + output_direction[2] = m_Parameters->m_F*output_direction[2] + (1-m_Parameters->m_F)*( (1-m_Parameters->m_G)*oldDir[2] + m_Parameters->m_G*(tensor[2]*oldDir[0] + tensor[4]*oldDir[1] + tensor[5]*oldDir[2])); output_direction.normalize(); } float a = 1; if (old_mag>0.5) a = dot_product(output_direction, oldDir); - if (a>=m_AngularThreshold) + if (a>=m_Parameters->GetAngularThresholdDot()) output_direction *= mag; else output_direction.fill(0); } else output_direction.fill(0); } catch(...) { } - if (m_FlipX) + if (m_Parameters->m_FlipX) output_direction[0] *= -1; - if (m_FlipY) + if (m_Parameters->m_FlipY) output_direction[1] *= -1; - if (m_FlipZ) + if (m_Parameters->m_FlipZ) output_direction[2] *= -1; + if (m_Parameters->m_ApplyDirectionMatrix) + output_direction = m_FloatImageRotation*output_direction; return output_direction; } } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.h b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.h index 5884ee8be1..6175bec856 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.h @@ -1,94 +1,80 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _TrackingHandlerTensor #define _TrackingHandlerTensor #include "mitkTrackingDataHandler.h" #include #include #include #include namespace mitk { /** * \brief Enables streamline tracking on tensor images. Supports multi tensor tracking by adding multiple tensor images. */ class MITKFIBERTRACKING_EXPORT TrackingHandlerTensor : public TrackingDataHandler { public: TrackingHandlerTensor(); ~TrackingHandlerTensor() override; typedef TensorImage::PixelType TensorType; typedef TensorImage::ItkTensorImageType ItkTensorImageType; typedef itk::Image< vnl_vector_fixed, 3> ItkPDImgType; void InitForTracking() override; ///< calls InputDataValidForTracking() and creates feature images vnl_vector_fixed ProposeDirection(const itk::Point& pos, std::deque< vnl_vector_fixed >& olddirs, itk::Index<3>& oldIndex) override; ///< predicts next progression direction at the given position bool WorldToIndex(itk::Point& pos, itk::Index<3>& index) override; - void SetF(float f){ m_F = f; } - void SetG(float g){ m_G = g; } - void SetFaThreshold(float FaThreshold){ m_FaThreshold = FaThreshold; } void AddTensorImage( ItkTensorImageType::ConstPointer img ){ m_TensorImages.push_back(img); DataModified(); } void SetTensorImage( ItkTensorImageType::ConstPointer img ){ m_TensorImages.clear(); m_TensorImages.push_back(img); DataModified(); } void ClearTensorImages(){ m_TensorImages.clear(); DataModified(); } void SetFaImage( ItkFloatImgType::Pointer img ){ m_FaImage = img; DataModified(); } void SetInterpolateTensors( bool interpolateTensors ){ m_InterpolateTensors = interpolateTensors; } - void SetMode( MODE m ) override - { - if (m==MODE::DETERMINISTIC) - m_Mode = m; - else - mitkThrow() << "Tensor tracker is only implemented for deterministic mode."; - } int GetNumTensorImages() const { return m_TensorImages.size(); } ItkUcharImgType::SpacingType GetSpacing() override{ return m_FaImage->GetSpacing(); } itk::Point GetOrigin() override{ return m_FaImage->GetOrigin(); } ItkUcharImgType::DirectionType GetDirection() override{ return m_FaImage->GetDirection(); } ItkUcharImgType::RegionType GetLargestPossibleRegion() override{ return m_FaImage->GetLargestPossibleRegion(); } protected: vnl_vector_fixed GetMatchingDirection(itk::Index<3> idx, vnl_vector_fixed& oldDir, int& image_num); vnl_vector_fixed GetDirection(itk::Point itkP, vnl_vector_fixed oldDir, TensorType& tensor); vnl_vector_fixed GetLargestEigenvector(TensorType& tensor); - float m_FaThreshold; - float m_F; - float m_G; - std::vector< ItkDoubleImgType::Pointer > m_EmaxImage; ///< Stores largest eigenvalues per voxel (one for each tensor) ItkFloatImgType::Pointer m_FaImage; ///< FA image used to determine streamline termination. std::vector< ItkPDImgType::Pointer > m_PdImage; ///< Stores principal direction of each tensor in each voxel. std::vector< ItkTensorImageType::ConstPointer > m_TensorImages; ///< Input tensor images. For multi tensor tracking provide multiple tensor images. bool m_InterpolateTensors; ///< If false, then the peaks are interpolated. Otherwiese, The tensors are interpolated. int m_NumberOfInputs; itk::LinearInterpolateImageFunction< itk::Image< float, 3 >, float >::Pointer m_FaInterpolator; }; } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp index 7d76d2624c..ae79b0851f 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.cpp @@ -1,1025 +1,980 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include "itkStreamlineTrackingFilter.h" #include #include #include #include "itkPointShell.h" #include #include #include #include #include #include #include #include namespace itk { StreamlineTrackingFilter ::StreamlineTrackingFilter() : m_PauseTracking(false) , m_AbortTracking(false) , m_BuildFibersFinished(false) , m_BuildFibersReady(0) , m_FiberPolyData(nullptr) , m_Points(nullptr) , m_Cells(nullptr) , m_StoppingRegions(nullptr) , m_TargetRegions(nullptr) , m_SeedImage(nullptr) , m_MaskImage(nullptr) , m_ExclusionRegions(nullptr) , m_OutputProbabilityMap(nullptr) - , m_MinVoxelSize(-1) - , m_AngularThresholdDeg(-1) - , m_StepSizeVox(-1) - , m_SamplingDistanceVox(-1) - , m_AngularThreshold(-1) - , m_StepSize(0) - , m_MaxLength(10000) - , m_MinTractLength(20.0) - , m_MaxTractLength(400.0) - , m_SeedsPerVoxel(1) - , m_AvoidStop(true) - , m_RandomSampling(false) - , m_SamplingDistance(-1) - , m_DeflectionMod(1.0) - , m_OnlyForwardSamples(true) - , m_UseStopVotes(true) - , m_NumberOfSamples(30) - , m_NumPreviousDirections(1) - , m_MaxNumTracts(-1) , m_Verbose(true) - , m_LoopCheck(-1) , m_DemoMode(false) - , m_Random(true) - , m_UseOutputProbabilityMap(false) , m_CurrentTracts(0) , m_Progress(0) , m_StopTracking(false) - , m_InterpolateMasks(true) - , m_TrialsPerSeed(10) - , m_EndpointConstraint(EndpointConstraints::NONE) - , m_IntroduceDirectionsFromPrior(true) - , m_TrackingPriorAsMask(true) - , m_TrackingPriorWeight(1.0) , m_TrackingPriorHandler(nullptr) { this->SetNumberOfRequiredInputs(0); } std::string StreamlineTrackingFilter::GetStatusText() { std::string status = "Seedpoints processed: " + boost::lexical_cast(m_Progress) + "/" + boost::lexical_cast(m_SeedPoints.size()); if (m_SeedPoints.size()>0) status += " (" + boost::lexical_cast(100*m_Progress/m_SeedPoints.size()) + "%)"; - if (m_MaxNumTracts>0) - status += "\nFibers accepted: " + boost::lexical_cast(m_CurrentTracts) + "/" + boost::lexical_cast(m_MaxNumTracts); + if (m_Parameters->m_MaxNumFibers>0) + status += "\nFibers accepted: " + boost::lexical_cast(m_CurrentTracts) + "/" + boost::lexical_cast(m_Parameters->m_MaxNumFibers); else status += "\nFibers accepted: " + boost::lexical_cast(m_CurrentTracts); return status; } void StreamlineTrackingFilter::BeforeTracking() { m_StopTracking = false; - m_TrackingHandler->SetRandom(m_Random); + m_TrackingHandler->SetParameters(m_Parameters); m_TrackingHandler->InitForTracking(); m_FiberPolyData = PolyDataType::New(); m_Points = vtkSmartPointer< vtkPoints >::New(); m_Cells = vtkSmartPointer< vtkCellArray >::New(); - itk::Vector< double, 3 > imageSpacing = m_TrackingHandler->GetSpacing(); - - if(imageSpacing[0](imageSpacing[0]); - else if (imageSpacing[1] < imageSpacing[2]) - m_MinVoxelSize = static_cast(imageSpacing[1]); - else - m_MinVoxelSize = static_cast(imageSpacing[2]); - - if (m_StepSizeVox(mitk::eps)) - m_StepSize = 0.5f*m_MinVoxelSize; - else - m_StepSize = m_StepSizeVox*m_MinVoxelSize; - - if (m_AngularThresholdDeg<0) - { - if (m_StepSize/m_MinVoxelSize<=0.966f) // minimum 15° for automatic estimation - m_AngularThreshold = static_cast(std::cos( 0.5 * itk::Math::pi * static_cast(m_StepSize/m_MinVoxelSize) )); - else - m_AngularThreshold = static_cast(std::cos( 0.5 * itk::Math::pi * 0.966 )); - } - else - m_AngularThreshold = static_cast(std::cos( static_cast(m_AngularThresholdDeg)*itk::Math::pi/180.0 )); - m_TrackingHandler->SetAngularThreshold(m_AngularThreshold); - if (m_TrackingPriorHandler!=nullptr) { - m_TrackingPriorHandler->SetRandom(m_Random); m_TrackingPriorHandler->InitForTracking(); - m_TrackingPriorHandler->SetAngularThreshold(m_AngularThreshold); } - if (m_SamplingDistanceVox(mitk::eps)) - m_SamplingDistance = m_MinVoxelSize*0.25f; - else - m_SamplingDistance = m_SamplingDistanceVox * m_MinVoxelSize; - m_PolyDataContainer.clear(); for (unsigned int i=0; iGetNumberOfThreads(); i++) { PolyDataType poly = PolyDataType::New(); m_PolyDataContainer.push_back(poly); } - if (m_UseOutputProbabilityMap) + auto imageSpacing = m_TrackingHandler->GetSpacing(); + if (m_Parameters->m_OutputProbMap) { m_OutputProbabilityMap = ItkDoubleImgType::New(); m_OutputProbabilityMap->SetSpacing(imageSpacing); m_OutputProbabilityMap->SetOrigin(m_TrackingHandler->GetOrigin()); m_OutputProbabilityMap->SetDirection(m_TrackingHandler->GetDirection()); m_OutputProbabilityMap->SetRegions(m_TrackingHandler->GetLargestPossibleRegion()); m_OutputProbabilityMap->Allocate(); m_OutputProbabilityMap->FillBuffer(0); } m_MaskInterpolator = itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::New(); m_StopInterpolator = itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::New(); m_SeedInterpolator = itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::New(); m_TargetInterpolator = itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::New(); m_ExclusionInterpolator = itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::New(); if (m_StoppingRegions.IsNull()) { m_StoppingRegions = ItkFloatImgType::New(); m_StoppingRegions->SetSpacing( imageSpacing ); m_StoppingRegions->SetOrigin( m_TrackingHandler->GetOrigin() ); m_StoppingRegions->SetDirection( m_TrackingHandler->GetDirection() ); m_StoppingRegions->SetRegions( m_TrackingHandler->GetLargestPossibleRegion() ); m_StoppingRegions->Allocate(); m_StoppingRegions->FillBuffer(0); } else std::cout << "StreamlineTracking - Using stopping region image" << std::endl; m_StopInterpolator->SetInputImage(m_StoppingRegions); if (m_ExclusionRegions.IsNotNull()) { std::cout << "StreamlineTracking - Using exclusion region image" << std::endl; m_ExclusionInterpolator->SetInputImage(m_ExclusionRegions); } if (m_TargetRegions.IsNull()) { m_TargetImageSet = false; m_TargetRegions = ItkFloatImgType::New(); m_TargetRegions->SetSpacing( imageSpacing ); m_TargetRegions->SetOrigin( m_TrackingHandler->GetOrigin() ); m_TargetRegions->SetDirection( m_TrackingHandler->GetDirection() ); m_TargetRegions->SetRegions( m_TrackingHandler->GetLargestPossibleRegion() ); m_TargetRegions->Allocate(); m_TargetRegions->FillBuffer(1); } else { m_TargetImageSet = true; m_TargetInterpolator->SetInputImage(m_TargetRegions); std::cout << "StreamlineTracking - Using target region image" << std::endl; } if (m_SeedImage.IsNull()) { m_SeedImageSet = false; m_SeedImage = ItkFloatImgType::New(); m_SeedImage->SetSpacing( imageSpacing ); m_SeedImage->SetOrigin( m_TrackingHandler->GetOrigin() ); m_SeedImage->SetDirection( m_TrackingHandler->GetDirection() ); m_SeedImage->SetRegions( m_TrackingHandler->GetLargestPossibleRegion() ); m_SeedImage->Allocate(); m_SeedImage->FillBuffer(1); } else { m_SeedImageSet = true; std::cout << "StreamlineTracking - Using seed image" << std::endl; } m_SeedInterpolator->SetInputImage(m_SeedImage); if (m_MaskImage.IsNull()) { // initialize mask image m_MaskImage = ItkFloatImgType::New(); m_MaskImage->SetSpacing( imageSpacing ); m_MaskImage->SetOrigin( m_TrackingHandler->GetOrigin() ); m_MaskImage->SetDirection( m_TrackingHandler->GetDirection() ); m_MaskImage->SetRegions( m_TrackingHandler->GetLargestPossibleRegion() ); m_MaskImage->Allocate(); m_MaskImage->FillBuffer(1); } else std::cout << "StreamlineTracking - Using mask image" << std::endl; m_MaskInterpolator->SetInputImage(m_MaskImage); // Autosettings for endpoint constraints - if (m_EndpointConstraint==EndpointConstraints::NONE && m_TargetImageSet && m_SeedImageSet) + if (m_Parameters->m_EpConstraints==EndpointConstraints::NONE && m_TargetImageSet && m_SeedImageSet) { MITK_INFO << "No endpoint constraint chosen but seed and target image set --> setting constraint to EPS_IN_SEED_AND_TARGET"; - m_EndpointConstraint = EndpointConstraints::EPS_IN_SEED_AND_TARGET; + m_Parameters->m_EpConstraints = EndpointConstraints::EPS_IN_SEED_AND_TARGET; } - else if (m_EndpointConstraint==EndpointConstraints::NONE && m_TargetImageSet) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::NONE && m_TargetImageSet) { MITK_INFO << "No endpoint constraint chosen but target image set --> setting constraint to EPS_IN_TARGET"; - m_EndpointConstraint = EndpointConstraints::EPS_IN_TARGET; + m_Parameters->m_EpConstraints = EndpointConstraints::EPS_IN_TARGET; } // Check if endpoint constraints are valid FiberType test_fib; itk::Point p; p.Fill(0); test_fib.push_back(p); test_fib.push_back(p); IsValidFiber(&test_fib); if (m_SeedPoints.empty()) GetSeedPointsFromSeedImage(); m_BuildFibersReady = 0; m_BuildFibersFinished = false; m_Tractogram.clear(); m_SamplingPointset = mitk::PointSet::New(); m_AlternativePointset = mitk::PointSet::New(); m_StopVotePointset = mitk::PointSet::New(); m_StartTime = std::chrono::system_clock::now(); if (m_DemoMode) omp_set_num_threads(1); - if (m_TrackingHandler->GetMode()==mitk::TrackingDataHandler::MODE::DETERMINISTIC) + if (m_Parameters->m_Mode==mitk::TrackingDataHandler::MODE::DETERMINISTIC) std::cout << "StreamlineTracking - Mode: deterministic" << std::endl; - else if(m_TrackingHandler->GetMode()==mitk::TrackingDataHandler::MODE::PROBABILISTIC) + else if(m_Parameters->m_Mode==mitk::TrackingDataHandler::MODE::PROBABILISTIC) { std::cout << "StreamlineTracking - Mode: probabilistic" << std::endl; - std::cout << "StreamlineTracking - Trials per seed: " << m_TrialsPerSeed << std::endl; + std::cout << "StreamlineTracking - Trials per seed: " << m_Parameters->m_TrialsPerSeed << std::endl; } else std::cout << "StreamlineTracking - Mode: ???" << std::endl; - if (m_EndpointConstraint==EndpointConstraints::NONE) + if (m_Parameters->m_EpConstraints==EndpointConstraints::NONE) std::cout << "StreamlineTracking - Endpoint constraint: NONE" << std::endl; - else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_TARGET) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: EPS_IN_TARGET" << std::endl; - else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_TARGET_LABELDIFF) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_TARGET_LABELDIFF) std::cout << "StreamlineTracking - Endpoint constraint: EPS_IN_TARGET_LABELDIFF" << std::endl; - else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_SEED_AND_TARGET) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_SEED_AND_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: EPS_IN_SEED_AND_TARGET" << std::endl; - else if (m_EndpointConstraint==EndpointConstraints::MIN_ONE_EP_IN_TARGET) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::MIN_ONE_EP_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: MIN_ONE_EP_IN_TARGET" << std::endl; - else if (m_EndpointConstraint==EndpointConstraints::ONE_EP_IN_TARGET) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::ONE_EP_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: ONE_EP_IN_TARGET" << std::endl; - else if (m_EndpointConstraint==EndpointConstraints::NO_EP_IN_TARGET) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::NO_EP_IN_TARGET) std::cout << "StreamlineTracking - Endpoint constraint: NO_EP_IN_TARGET" << std::endl; - std::cout << "StreamlineTracking - Angular threshold: " << m_AngularThreshold << " (" << 180*std::acos( static_cast(m_AngularThreshold) )/itk::Math::pi << "°)" << std::endl; - std::cout << "StreamlineTracking - Stepsize: " << m_StepSize << "mm (" << m_StepSize/m_MinVoxelSize << "*vox)" << std::endl; - std::cout << "StreamlineTracking - Seeds per voxel: " << m_SeedsPerVoxel << std::endl; - std::cout << "StreamlineTracking - Max. tract length: " << m_MaxTractLength << "mm" << std::endl; - std::cout << "StreamlineTracking - Min. tract length: " << m_MinTractLength << "mm" << std::endl; - std::cout << "StreamlineTracking - Max. num. tracts: " << m_MaxNumTracts << std::endl; - std::cout << "StreamlineTracking - Loop check: " << m_LoopCheck << "°" << std::endl; + std::cout << "StreamlineTracking - Angular threshold: " << m_Parameters->GetAngularThresholdDot() << "°" << std::endl; + std::cout << "StreamlineTracking - Stepsize: " << m_Parameters->GetStepSizeMm() << "mm (" << m_Parameters->GetStepSizeMm()/m_Parameters->GetMinVoxelSizeMm() << "*vox)" << std::endl; + std::cout << "StreamlineTracking - Seeds per voxel: " << m_Parameters->m_SeedsPerVoxel << std::endl; + std::cout << "StreamlineTracking - Max. tract length: " << m_Parameters->m_MaxTractLengthMm << "mm" << std::endl; + std::cout << "StreamlineTracking - Min. tract length: " << m_Parameters->m_MinTractLengthMm << "mm" << std::endl; + std::cout << "StreamlineTracking - Max. num. tracts: " << m_Parameters->m_MaxNumFibers << std::endl; + std::cout << "StreamlineTracking - Loop check: " << m_Parameters->GetLoopCheckDeg() << "°" << std::endl; - std::cout << "StreamlineTracking - Num. neighborhood samples: " << m_NumberOfSamples << std::endl; - std::cout << "StreamlineTracking - Max. sampling distance: " << m_SamplingDistance << "mm (" << m_SamplingDistance/m_MinVoxelSize << "*vox)" << std::endl; - std::cout << "StreamlineTracking - Deflection modifier: " << m_DeflectionMod << std::endl; + std::cout << "StreamlineTracking - Num. neighborhood samples: " << m_Parameters->m_NumSamples << std::endl; + std::cout << "StreamlineTracking - Max. sampling distance: " << m_Parameters->GetSamplingDistanceMm() << "mm (" << m_Parameters->GetSamplingDistanceMm()/m_Parameters->GetMinVoxelSizeMm() << "*vox)" << std::endl; + std::cout << "StreamlineTracking - Deflection modifier: " << m_Parameters->m_DeflectionMod << std::endl; - std::cout << "StreamlineTracking - Use stop votes: " << m_UseStopVotes << std::endl; - std::cout << "StreamlineTracking - Only frontal samples: " << m_OnlyForwardSamples << std::endl; + std::cout << "StreamlineTracking - Use stop votes: " << m_Parameters->m_StopVotes << std::endl; + std::cout << "StreamlineTracking - Only frontal samples: " << m_Parameters->m_OnlyForwardSamples << std::endl; if (m_TrackingPriorHandler!=nullptr) - std::cout << "StreamlineTracking - Using directional prior for tractography (w=" << m_TrackingPriorWeight << ")" << std::endl; + std::cout << "StreamlineTracking - Using directional prior for tractography (w=" << m_Parameters->m_Weight << ")" << std::endl; if (m_DemoMode) { std::cout << "StreamlineTracking - Running in demo mode"; std::cout << "StreamlineTracking - Starting streamline tracking using 1 thread" << std::endl; } else std::cout << "StreamlineTracking - Starting streamline tracking using " << omp_get_max_threads() << " threads" << std::endl; } void StreamlineTrackingFilter::CalculateNewPosition(itk::Point& pos, vnl_vector_fixed& dir) { - pos[0] += dir[0]*m_StepSize; - pos[1] += dir[1]*m_StepSize; - pos[2] += dir[2]*m_StepSize; + pos[0] += dir[0]*m_Parameters->GetStepSizeMm(); + pos[1] += dir[1]*m_Parameters->GetStepSizeMm(); + pos[2] += dir[2]*m_Parameters->GetStepSizeMm(); } std::vector< vnl_vector_fixed > StreamlineTrackingFilter::CreateDirections(unsigned int NPoints) { std::vector< vnl_vector_fixed > pointshell; if (NPoints<2) return pointshell; std::vector< double > theta; theta.resize(NPoints); std::vector< double > phi; phi.resize(NPoints); auto C = sqrt(4*itk::Math::pi); phi[0] = 0.0; phi[NPoints-1] = 0.0; for(unsigned int i=0; i0 && i d; d[0] = static_cast(cos(theta[i]) * cos(phi[i])); d[1] = static_cast(cos(theta[i]) * sin(phi[i])); d[2] = static_cast(sin(theta[i])); pointshell.push_back(d); } return pointshell; } vnl_vector_fixed StreamlineTrackingFilter::GetNewDirection(const itk::Point &pos, std::deque >& olddirs, itk::Index<3> &oldIndex) { if (m_DemoMode) { m_SamplingPointset->Clear(); m_AlternativePointset->Clear(); m_StopVotePointset->Clear(); } vnl_vector_fixed direction; direction.fill(0); - if (mitk::imv::IsInsideMask(pos, m_InterpolateMasks, m_MaskInterpolator) && !mitk::imv::IsInsideMask(pos, m_InterpolateMasks, m_StopInterpolator)) + if (mitk::imv::IsInsideMask(pos, m_Parameters->m_InterpolateRoiImages, m_MaskInterpolator) && !mitk::imv::IsInsideMask(pos, m_Parameters->m_InterpolateRoiImages, m_StopInterpolator)) direction = m_TrackingHandler->ProposeDirection(pos, olddirs, oldIndex); // get direction proposal at current streamline position else return direction; int stop_votes = 0; int possible_stop_votes = 0; if (!olddirs.empty()) { vnl_vector_fixed olddir = olddirs.back(); - std::vector< vnl_vector_fixed > probeVecs = CreateDirections(m_NumberOfSamples); + std::vector< vnl_vector_fixed > probeVecs = CreateDirections(m_Parameters->m_NumSamples); itk::Point sample_pos; unsigned int alternatives = 1; for (unsigned int i=0; i d; bool is_stop_voter = false; - if (m_Random && m_RandomSampling) + if (!m_Parameters->m_FixRandomSeed && m_Parameters->m_RandomSampling) { d[0] = static_cast(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); d[1] = static_cast(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); d[2] = static_cast(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); d.normalize(); - d *= static_cast(m_TrackingHandler->GetRandDouble(0, static_cast(m_SamplingDistance))); + d *= static_cast(m_TrackingHandler->GetRandDouble(0, static_cast(m_Parameters->GetSamplingDistanceMm()))); } else { d = probeVecs.at(i); float dot = dot_product(d, olddir); - if (m_UseStopVotes && dot>0.7f) + if (m_Parameters->m_StopVotes && dot>0.7f) { is_stop_voter = true; possible_stop_votes++; } - else if (m_OnlyForwardSamples && dot<0) + else if (m_Parameters->m_OnlyForwardSamples && dot<0) continue; - d *= m_SamplingDistance; + d *= m_Parameters->GetSamplingDistanceMm(); } sample_pos[0] = pos[0] + d[0]; sample_pos[1] = pos[1] + d[1]; sample_pos[2] = pos[2] + d[2]; vnl_vector_fixed tempDir; tempDir.fill(0.0); - if (mitk::imv::IsInsideMask(sample_pos, m_InterpolateMasks, m_MaskInterpolator)) + if (mitk::imv::IsInsideMask(sample_pos, m_Parameters->m_InterpolateRoiImages, m_MaskInterpolator)) tempDir = m_TrackingHandler->ProposeDirection(sample_pos, olddirs, oldIndex); // sample neighborhood if (tempDir.magnitude()>static_cast(mitk::eps)) { direction += tempDir; if(m_DemoMode) m_SamplingPointset->InsertPoint(i, sample_pos); } - else if (m_AvoidStop && olddir.magnitude()>0.5f) // out of white matter + else if (m_Parameters->m_AvoidStop && olddir.magnitude()>0.5f) // out of white matter { if (is_stop_voter) stop_votes++; if (m_DemoMode) m_StopVotePointset->InsertPoint(i, sample_pos); float dot = dot_product(d, olddir); if (dot >= 0.0f) // in front of plane defined by pos and olddir d = -d + 2*dot*olddir; // reflect else d = -d; // invert // look a bit further into the other direction sample_pos[0] = pos[0] + d[0]; sample_pos[1] = pos[1] + d[1]; sample_pos[2] = pos[2] + d[2]; alternatives++; vnl_vector_fixed tempDir; tempDir.fill(0.0); - if (mitk::imv::IsInsideMask(sample_pos, m_InterpolateMasks, m_MaskInterpolator)) + if (mitk::imv::IsInsideMask(sample_pos, m_Parameters->m_InterpolateRoiImages, m_MaskInterpolator)) tempDir = m_TrackingHandler->ProposeDirection(sample_pos, olddirs, oldIndex); // sample neighborhood if (tempDir.magnitude()>static_cast(mitk::eps)) // are we back in the white matter? { - direction += d * m_DeflectionMod; // go into the direction of the white matter + direction += d * m_Parameters->m_DeflectionMod; // go into the direction of the white matter direction += tempDir; // go into the direction of the white matter direction at this location if(m_DemoMode) m_AlternativePointset->InsertPoint(alternatives, sample_pos); } else { if (m_DemoMode) m_StopVotePointset->InsertPoint(i, sample_pos); } } else { if (m_DemoMode) m_StopVotePointset->InsertPoint(i, sample_pos); if (is_stop_voter) stop_votes++; } } } bool valid = false; if (direction.magnitude()>0.001f && (possible_stop_votes==0 || static_cast(stop_votes)/possible_stop_votes<0.5f) ) { direction.normalize(); valid = true; } else direction.fill(0); - if (m_TrackingPriorHandler!=nullptr && (m_IntroduceDirectionsFromPrior || valid)) + if (m_TrackingPriorHandler!=nullptr && (m_Parameters->m_NewDirectionsFromPrior || valid)) { vnl_vector_fixed prior = m_TrackingPriorHandler->ProposeDirection(pos, olddirs, oldIndex); if (prior.magnitude()>0.001f) { prior.normalize(); if (dot_product(prior,direction)<0) prior *= -1; - direction = (1.0f-m_TrackingPriorWeight) * direction + m_TrackingPriorWeight * prior; + direction = (1.0f-m_Parameters->m_Weight) * direction + m_Parameters->m_Weight * prior; direction.normalize(); } - else if (m_TrackingPriorAsMask) + else if (m_Parameters->m_RestrictToPrior) direction.fill(0.0); } return direction; } float StreamlineTrackingFilter::FollowStreamline(itk::Point pos, vnl_vector_fixed dir, FiberType* fib, DirectionContainer* container, float tractLength, bool front, bool &exclude) { vnl_vector_fixed zero_dir; zero_dir.fill(0.0); std::deque< vnl_vector_fixed > last_dirs; - for (unsigned int i=0; im_NumPreviousDirections-1; i++) last_dirs.push_back(zero_dir); - for (int step=0; step< m_MaxLength/2; step++) + for (int step=0; step< 5000; step++) { itk::Index<3> oldIndex; m_TrackingHandler->WorldToIndex(pos, oldIndex); // get new position CalculateNewPosition(pos, dir); - if (m_ExclusionRegions.IsNotNull() && mitk::imv::IsInsideMask(pos, m_InterpolateMasks, m_ExclusionInterpolator)) + if (m_ExclusionRegions.IsNotNull() && mitk::imv::IsInsideMask(pos, m_Parameters->m_InterpolateRoiImages, m_ExclusionInterpolator)) { exclude = true; return tractLength; } if (m_AbortTracking) return tractLength; // if yes, add new point to streamline dir.normalize(); if (front) { fib->push_front(pos); container->push_front(dir); } else { fib->push_back(pos); container->push_back(dir); } - tractLength += m_StepSize; + tractLength += m_Parameters->GetStepSizeMm(); - if (m_LoopCheck>=0 && CheckCurvature(container, front)>m_LoopCheck) + if (m_Parameters->GetLoopCheckDeg()>=0 && CheckCurvature(container, front)>m_Parameters->GetLoopCheckDeg()) return tractLength; - if (tractLength>m_MaxTractLength) + if (tractLength>m_Parameters->m_MaxTractLengthMm) return tractLength; - if (m_DemoMode && !m_UseOutputProbabilityMap) // CHECK: warum sind die samplingpunkte der streamline in der visualisierung immer einen schritt voras? + if (m_DemoMode && !m_Parameters->m_OutputProbMap) // CHECK: warum sind die samplingpunkte der streamline in der visualisierung immer einen schritt voras? { #pragma omp critical { m_BuildFibersReady++; m_Tractogram.push_back(*fib); BuildFibers(true); m_Stop = true; while (m_Stop){ } } } last_dirs.push_back(dir); - if (last_dirs.size()>m_NumPreviousDirections) + if (last_dirs.size()>m_Parameters->m_NumPreviousDirections) last_dirs.pop_front(); dir = GetNewDirection(pos, last_dirs, oldIndex); while (m_PauseTracking){} if (dir.magnitude()<0.0001f) return tractLength; } return tractLength; } float StreamlineTrackingFilter::CheckCurvature(DirectionContainer* fib, bool front) { if (fib->size()<8) return 0; - float m_Distance = std::max(m_MinVoxelSize*4, m_StepSize*8); + float m_Distance = std::max(m_Parameters->GetMinVoxelSizeMm()*4, m_Parameters->GetStepSizeMm()*8); float dist = 0; std::vector< vnl_vector_fixed< float, 3 > > vectors; vnl_vector_fixed< float, 3 > meanV; meanV.fill(0); float dev = 0; if (front) { int c = 0; while(dist(fib->size())-1) { - dist += m_StepSize; + dist += m_Parameters->GetStepSizeMm(); vnl_vector_fixed< float, 3 > v = fib->at(static_cast(c)); if (dot_product(v,meanV)<0) v = -v; vectors.push_back(v); meanV += v; c++; } } else { int c = static_cast(fib->size())-1; while(dist=0) { - dist += m_StepSize; + dist += m_Parameters->GetStepSizeMm(); vnl_vector_fixed< float, 3 > v = fib->at(static_cast(c)); if (dot_product(v,meanV)<0) v = -v; vectors.push_back(v); meanV += v; c--; } } meanV.normalize(); for (unsigned int c=0; c1.0f) angle = 1.0; dev += acos(angle)*180.0f/static_cast(itk::Math::pi); } if (vectors.size()>0) dev /= vectors.size(); return dev; } +std::shared_ptr StreamlineTrackingFilter::GetParameters() const +{ + return m_Parameters; +} + +void StreamlineTrackingFilter::SetParameters(std::shared_ptr< mitk::StreamlineTractographyParameters > Parameters) +{ + m_Parameters = Parameters; +} + void StreamlineTrackingFilter::SetTrackingPriorHandler(mitk::TrackingDataHandler *TrackingPriorHandler) { m_TrackingPriorHandler = TrackingPriorHandler; } void StreamlineTrackingFilter::GetSeedPointsFromSeedImage() { MITK_INFO << "StreamlineTracking - Calculating seed points."; m_SeedPoints.clear(); typedef ImageRegionConstIterator< ItkFloatImgType > MaskIteratorType; MaskIteratorType sit(m_SeedImage, m_SeedImage->GetLargestPossibleRegion()); sit.GoToBegin(); while (!sit.IsAtEnd()) { if (sit.Value()>0) { ItkFloatImgType::IndexType index = sit.GetIndex(); itk::ContinuousIndex start; start[0] = index[0]; start[1] = index[1]; start[2] = index[2]; itk::Point worldPos; m_SeedImage->TransformContinuousIndexToPhysicalPoint(start, worldPos); - if ( mitk::imv::IsInsideMask(worldPos, m_InterpolateMasks, m_MaskInterpolator) ) + if ( mitk::imv::IsInsideMask(worldPos, m_Parameters->m_InterpolateRoiImages, m_MaskInterpolator) ) { m_SeedPoints.push_back(worldPos); - for (int s = 1; s < m_SeedsPerVoxel; s++) + for (unsigned int s = 1; s < m_Parameters->m_SeedsPerVoxel; s++) { start[0] = index[0] + static_cast(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); start[1] = index[1] + static_cast(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); start[2] = index[2] + static_cast(m_TrackingHandler->GetRandDouble(-0.5, 0.5)); itk::Point worldPos; m_SeedImage->TransformContinuousIndexToPhysicalPoint(start, worldPos); m_SeedPoints.push_back(worldPos); } } } ++sit; } if (m_SeedPoints.empty()) mitkThrow() << "No valid seed point in seed image! Is your seed image registered with the image you are tracking on?"; } void StreamlineTrackingFilter::GenerateData() { this->BeforeTracking(); - if (m_Random) + if (!m_Parameters->m_FixRandomSeed) std::random_shuffle(m_SeedPoints.begin(), m_SeedPoints.end()); m_CurrentTracts = 0; int num_seeds = static_cast(m_SeedPoints.size()); itk::Index<3> zeroIndex; zeroIndex.Fill(0); m_Progress = 0; int i = 0; int print_interval = num_seeds/100; if (print_interval<100) m_Verbose=false; + unsigned int trials_per_seed = 1; + if(m_Parameters->m_Mode==mitk::TrackingDataHandler::MODE::PROBABILISTIC) + trials_per_seed = m_Parameters->m_TrialsPerSeed; + #pragma omp parallel while (i=num_seeds || m_StopTracking) continue; else if (m_Verbose && i%print_interval==0) #pragma omp critical { m_Progress += static_cast(print_interval); std::cout << " \r"; - if (m_MaxNumTracts>0) - std::cout << "Tried: " << m_Progress << "/" << num_seeds << " | Accepted: " << m_CurrentTracts << "/" << m_MaxNumTracts << '\r'; + if (m_Parameters->m_MaxNumFibers>0) + std::cout << "Tried: " << m_Progress << "/" << num_seeds << " | Accepted: " << m_CurrentTracts << "/" << m_Parameters->m_MaxNumFibers << '\r'; else std::cout << "Tried: " << m_Progress << "/" << num_seeds << " | Accepted: " << m_CurrentTracts << '\r'; cout.flush(); } const itk::Point worldPos = m_SeedPoints.at(static_cast(temp_i)); - for (unsigned int trials=0; trials dir; dir.fill(0.0); std::deque< vnl_vector_fixed > olddirs; dir = GetNewDirection(worldPos, olddirs, zeroIndex) * 0.5f; bool exclude = false; - if (m_ExclusionRegions.IsNotNull() && mitk::imv::IsInsideMask(worldPos, m_InterpolateMasks, m_ExclusionInterpolator)) + if (m_ExclusionRegions.IsNotNull() && mitk::imv::IsInsideMask(worldPos, m_Parameters->m_InterpolateRoiImages, m_ExclusionInterpolator)) exclude = true; bool success = false; if (dir.magnitude()>0.0001f && !exclude) { // forward tracking tractLength = FollowStreamline(worldPos, dir, &fib, &direction_container, 0, false, exclude); fib.push_front(worldPos); // backward tracking if (!exclude) tractLength = FollowStreamline(worldPos, -dir, &fib, &direction_container, tractLength, true, exclude); counter = fib.size(); - if (tractLength>=m_MinTractLength && counter>=2 && !exclude) + if (tractLength>=m_Parameters->m_MinTractLengthMm && counter>=2 && !exclude) { #pragma omp critical if ( IsValidFiber(&fib) ) { if (!m_StopTracking) { - if (!m_UseOutputProbabilityMap) + if (!m_Parameters->m_OutputProbMap) m_Tractogram.push_back(fib); else FiberToProbmap(&fib); m_CurrentTracts++; success = true; } - if (m_MaxNumTracts > 0 && m_CurrentTracts>=static_cast(m_MaxNumTracts)) + if (m_Parameters->m_MaxNumFibers > 0 && m_CurrentTracts>=static_cast(m_Parameters->m_MaxNumFibers)) { if (!m_StopTracking) { std::cout << " \r"; MITK_INFO << "Reconstructed maximum number of tracts (" << m_CurrentTracts << "). Stopping tractography."; } m_StopTracking = true; } } } } - if (success || m_TrackingHandler->GetMode()!=mitk::TrackingDataHandler::PROBABILISTIC) + if (success || m_Parameters->m_Mode!=MODE::PROBABILISTIC) break; // we only try one seed point multiple times if we use a probabilistic tracker and have not found a valid streamline yet }// trials per seed }// seed points this->AfterTracking(); } bool StreamlineTrackingFilter::IsValidFiber(FiberType* fib) { - if (m_EndpointConstraint==EndpointConstraints::NONE) + if (m_Parameters->m_EpConstraints==EndpointConstraints::NONE) { return true; } - else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_TARGET) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_TARGET) { if (m_TargetImageSet) { - if ( mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_TargetInterpolator) - && mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_TargetInterpolator) ) + if ( mitk::imv::IsInsideMask(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) + && mitk::imv::IsInsideMask(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint EPS_IN_TARGET chosen!"; } - else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_TARGET_LABELDIFF) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_TARGET_LABELDIFF) { if (m_TargetImageSet) { float v1 = mitk::imv::GetImageValue(fib->front(), false, m_TargetInterpolator); float v2 = mitk::imv::GetImageValue(fib->back(), false, m_TargetInterpolator); if ( v1>0.0f && v2>0.0f && v1!=v2 ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint EPS_IN_TARGET_LABELDIFF chosen!"; } - else if (m_EndpointConstraint==EndpointConstraints::EPS_IN_SEED_AND_TARGET) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::EPS_IN_SEED_AND_TARGET) { if (m_TargetImageSet && m_SeedImageSet) { - if ( mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_SeedInterpolator) - && mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_TargetInterpolator) ) + if ( mitk::imv::IsInsideMask(fib->front(), m_Parameters->m_InterpolateRoiImages, m_SeedInterpolator) + && mitk::imv::IsInsideMask(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; - if ( mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_SeedInterpolator) - && mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_TargetInterpolator) ) + if ( mitk::imv::IsInsideMask(fib->back(), m_Parameters->m_InterpolateRoiImages, m_SeedInterpolator) + && mitk::imv::IsInsideMask(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; return false; } else mitkThrow() << "No target or seed image set but endpoint constraint EPS_IN_SEED_AND_TARGET chosen!"; } - else if (m_EndpointConstraint==EndpointConstraints::MIN_ONE_EP_IN_TARGET) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::MIN_ONE_EP_IN_TARGET) { if (m_TargetImageSet) { - if ( mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_TargetInterpolator) - || mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_TargetInterpolator) ) + if ( mitk::imv::IsInsideMask(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) + || mitk::imv::IsInsideMask(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint MIN_ONE_EP_IN_TARGET chosen!"; } - else if (m_EndpointConstraint==EndpointConstraints::ONE_EP_IN_TARGET) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::ONE_EP_IN_TARGET) { if (m_TargetImageSet) { - if ( mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_TargetInterpolator) - && !mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_TargetInterpolator) ) + if ( mitk::imv::IsInsideMask(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) + && !mitk::imv::IsInsideMask(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; - if ( !mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_TargetInterpolator) - && mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_TargetInterpolator) ) + if ( !mitk::imv::IsInsideMask(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) + && mitk::imv::IsInsideMask(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return true; return false; } else mitkThrow() << "No target image set but endpoint constraint ONE_EP_IN_TARGET chosen!"; } - else if (m_EndpointConstraint==EndpointConstraints::NO_EP_IN_TARGET) + else if (m_Parameters->m_EpConstraints==EndpointConstraints::NO_EP_IN_TARGET) { if (m_TargetImageSet) { - if ( mitk::imv::IsInsideMask(fib->front(), m_InterpolateMasks, m_TargetInterpolator) - || mitk::imv::IsInsideMask(fib->back(), m_InterpolateMasks, m_TargetInterpolator) ) + if ( mitk::imv::IsInsideMask(fib->front(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) + || mitk::imv::IsInsideMask(fib->back(), m_Parameters->m_InterpolateRoiImages, m_TargetInterpolator) ) return false; return true; } else mitkThrow() << "No target image set but endpoint constraint NO_EP_IN_TARGET chosen!"; } return true; } void StreamlineTrackingFilter::FiberToProbmap(FiberType* fib) { ItkDoubleImgType::IndexType last_idx; last_idx.Fill(0); for (auto p : *fib) { ItkDoubleImgType::IndexType idx; m_OutputProbabilityMap->TransformPhysicalPointToIndex(p, idx); if (idx != last_idx) { if (m_OutputProbabilityMap->GetLargestPossibleRegion().IsInside(idx)) m_OutputProbabilityMap->SetPixel(idx, m_OutputProbabilityMap->GetPixel(idx)+1); last_idx = idx; } } } void StreamlineTrackingFilter::BuildFibers(bool check) { if (m_BuildFibersReady::New(); vtkSmartPointer vNewLines = vtkSmartPointer::New(); vtkSmartPointer vNewPoints = vtkSmartPointer::New(); for (unsigned int i=0; i container = vtkSmartPointer::New(); FiberType fib = m_Tractogram.at(i); for (FiberType::iterator it = fib.begin(); it!=fib.end(); ++it) { vtkIdType id = vNewPoints->InsertNextPoint((*it).GetDataPointer()); container->GetPointIds()->InsertNextId(id); } vNewLines->InsertNextCell(container); } if (check) for (int i=0; iSetPoints(vNewPoints); m_FiberPolyData->SetLines(vNewLines); m_BuildFibersFinished = true; } void StreamlineTrackingFilter::AfterTracking() { if (m_Verbose) std::cout << " \r"; - if (!m_UseOutputProbabilityMap) + if (!m_Parameters->m_OutputProbMap) { MITK_INFO << "Reconstructed " << m_Tractogram.size() << " fibers."; MITK_INFO << "Generating polydata "; BuildFibers(false); } else { itk::RescaleIntensityImageFilter< ItkDoubleImgType, ItkDoubleImgType >::Pointer filter = itk::RescaleIntensityImageFilter< ItkDoubleImgType, ItkDoubleImgType >::New(); filter->SetInput(m_OutputProbabilityMap); filter->SetOutputMaximum(1.0); filter->SetOutputMinimum(0.0); filter->Update(); m_OutputProbabilityMap = filter->GetOutput(); } MITK_INFO << "done"; m_EndTime = std::chrono::system_clock::now(); std::chrono::hours hh = std::chrono::duration_cast(m_EndTime - m_StartTime); std::chrono::minutes mm = std::chrono::duration_cast(m_EndTime - m_StartTime); std::chrono::seconds ss = std::chrono::duration_cast(m_EndTime - m_StartTime); mm %= 60; ss %= 60; MITK_INFO << "Tracking took " << hh.count() << "h, " << mm.count() << "m and " << ss.count() << "s"; m_SeedPoints.clear(); } void StreamlineTrackingFilter::SetDicomProperties(mitk::FiberBundle::Pointer fib) { std::string model_code_value = "-"; std::string model_code_meaning = "-"; std::string algo_code_value = "-"; std::string algo_code_meaning = "-"; - if (m_TrackingHandler->GetMode()==mitk::TrackingDataHandler::DETERMINISTIC && dynamic_cast(m_TrackingHandler) && !m_TrackingHandler->GetInterpolate()) + if ( m_Parameters->m_Mode==MODE::DETERMINISTIC && dynamic_cast(m_TrackingHandler) && !m_Parameters->m_InterpolateTractographyData) { algo_code_value = "sup181_ee04"; algo_code_meaning = "FACT"; } - else if (m_TrackingHandler->GetMode()==mitk::TrackingDataHandler::DETERMINISTIC) + else if (m_Parameters->m_Mode==MODE::DETERMINISTIC) { algo_code_value = "sup181_ee01"; algo_code_meaning = "Deterministic"; } - else if (m_TrackingHandler->GetMode()==mitk::TrackingDataHandler::PROBABILISTIC) + else if (m_Parameters->m_Mode==MODE::PROBABILISTIC) { algo_code_value = "sup181_ee02"; algo_code_meaning = "Probabilistic"; } if (dynamic_cast(m_TrackingHandler) || (dynamic_cast(m_TrackingHandler) && dynamic_cast(m_TrackingHandler)->GetIsOdfFromTensor() ) ) { if ( dynamic_cast(m_TrackingHandler) && dynamic_cast(m_TrackingHandler)->GetNumTensorImages()>1 ) { model_code_value = "sup181_bb02"; model_code_meaning = "Multi Tensor"; } else { model_code_value = "sup181_bb01"; model_code_meaning = "Single Tensor"; } } else if (dynamic_cast*>(m_TrackingHandler) || dynamic_cast*>(m_TrackingHandler)) { model_code_value = "sup181_bb03"; model_code_meaning = "Model Free"; } else if (dynamic_cast(m_TrackingHandler)) { model_code_value = "-"; model_code_meaning = "ODF"; } else if (dynamic_cast(m_TrackingHandler)) { model_code_value = "-"; model_code_meaning = "Peaks"; } fib->SetProperty("DICOM.anatomy.value", mitk::StringProperty::New("T-A0095")); fib->SetProperty("DICOM.anatomy.meaning", mitk::StringProperty::New("White matter of brain and spinal cord")); fib->SetProperty("DICOM.algo_code.value", mitk::StringProperty::New(algo_code_value)); fib->SetProperty("DICOM.algo_code.meaning", mitk::StringProperty::New(algo_code_meaning)); fib->SetProperty("DICOM.model_code.value", mitk::StringProperty::New(model_code_value)); fib->SetProperty("DICOM.model_code.meaning", mitk::StringProperty::New(model_code_meaning)); } } diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.h b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.h index 3817102215..ef6c4f7adc 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.h +++ b/Modules/DiffusionImaging/FiberTracking/Algorithms/itkStreamlineTrackingFilter.h @@ -1,258 +1,195 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __itkMLBSTrackingFilter_h_ #define __itkMLBSTrackingFilter_h_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include namespace itk{ /** * \brief Performs streamline tracking on the input image. Depending on the tracking handler this can be a tensor, peak or machine learning based tracking. */ class MITKFIBERTRACKING_EXPORT StreamlineTrackingFilter : public ProcessObject { public: - enum EndpointConstraints { - NONE, ///< No constraints on endpoint locations - EPS_IN_TARGET, ///< Both EPs are required to be located in the target image - EPS_IN_TARGET_LABELDIFF, ///< Both EPs are required to be located in the target image and the image values at the respective position needs to be distinct - EPS_IN_SEED_AND_TARGET, ///< One EP is required to be located in the seed image and one in the target image - MIN_ONE_EP_IN_TARGET, ///< At least one EP is required to be located in the target image - ONE_EP_IN_TARGET, ///< Exactly one EP is required to be located in the target image - NO_EP_IN_TARGET ///< No EP is allowed to be located in the target image - }; - typedef StreamlineTrackingFilter Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef ProcessObject Superclass; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Runtime information support. */ itkTypeMacro(MLBSTrackingFilter, ImageToImageFilter) typedef itk::Image ItkUcharImgType; typedef itk::Image ItkUintImgType; typedef itk::Image ItkDoubleImgType; typedef itk::Image ItkFloatImgType; typedef vtkSmartPointer< vtkPolyData > PolyDataType; + typedef mitk::StreamlineTractographyParameters::EndpointConstraints EndpointConstraints; + typedef mitk::StreamlineTractographyParameters::MODE MODE; typedef std::deque< vnl_vector_fixed > DirectionContainer; typedef std::deque< itk::Point > FiberType; typedef std::vector< FiberType > BundleType; volatile bool m_PauseTracking; bool m_AbortTracking; bool m_BuildFibersFinished; int m_BuildFibersReady; volatile bool m_Stop; mitk::PointSet::Pointer m_SamplingPointset; mitk::PointSet::Pointer m_StopVotePointset; mitk::PointSet::Pointer m_AlternativePointset; - void SetStepSize(float v) ///< Integration step size in voxels, default is 0.5 * voxel - { m_StepSizeVox = v; } - void SetAngularThreshold(float v) ///< Angular threshold per step (in degree), default is 90deg x stepsize - { m_AngularThresholdDeg = v; } - void SetSamplingDistance(float v) ///< Maximum distance of sampling points in voxels, default is 0.25 * voxel - { m_SamplingDistanceVox = v; } - void SetDicomProperties(mitk::FiberBundle::Pointer fib); itkGetMacro( OutputProbabilityMap, ItkDoubleImgType::Pointer) ///< Output probability map itkGetMacro( FiberPolyData, PolyDataType ) ///< Output fibers - itkGetMacro( UseOutputProbabilityMap, bool) - itkGetMacro( MinVoxelSize, float) - itkGetMacro( EndpointConstraint, EndpointConstraints) itkSetMacro( SeedImage, ItkFloatImgType::Pointer) ///< Seeds are only placed inside of this mask. itkSetMacro( MaskImage, ItkFloatImgType::Pointer) ///< Tracking is only performed inside of this mask image. itkSetMacro( ExclusionRegions, ItkFloatImgType::Pointer)///< Fibers passing any of the ROIs in this image are discarded. - itkSetMacro( SeedsPerVoxel, int) ///< One seed placed in the center of each voxel or multiple seeds randomly placed inside each voxel. - itkSetMacro( MinTractLength, float ) ///< Shorter tracts are discarded. - itkSetMacro( MaxTractLength, float ) ///< Streamline progression stops if tract is longer than specified. - itkSetMacro( EndpointConstraint, EndpointConstraints) ///< Determines what fibers are accepted based on their endpoint location - - itkSetMacro( UseStopVotes, bool ) ///< Frontal sampling points can vote for stopping the streamline even if the remaining sampling points keep pushing - itkSetMacro( OnlyForwardSamples, bool ) ///< Don't use sampling points behind the current position in progression direction - itkSetMacro( DeflectionMod, float ) ///< Deflection distance modifier + itkSetMacro( StoppingRegions, ItkFloatImgType::Pointer) ///< Streamlines entering a stopping region will stop immediately itkSetMacro( TargetRegions, ItkFloatImgType::Pointer) ///< Only streamline starting and ending in this mask are retained itkSetMacro( DemoMode, bool ) - itkSetMacro( NumberOfSamples, unsigned int ) ///< Number of neighborhood sampling points - itkSetMacro( LoopCheck, float ) ///< Checks fiber curvature (angular deviation across 5mm) is larger than 30°. If yes, the streamline progression is stopped. - itkSetMacro( AvoidStop, bool ) ///< Use additional sampling points to avoid premature streamline termination - itkSetMacro( RandomSampling, bool ) ///< If true, the sampling points are distributed randomly around the current position, not sphericall in the specified sampling distance. - itkSetMacro( NumPreviousDirections, unsigned int ) ///< How many "old" steps do we want to consider in our decision where to go next? - itkSetMacro( MaxNumTracts, int ) ///< Tracking is stopped if the maximum number of tracts is exceeded - itkSetMacro( Random, bool ) ///< If true, seedpoints are shuffled randomly before tracking itkSetMacro( Verbose, bool ) ///< If true, output tracking progress (might be slower) - itkSetMacro( UseOutputProbabilityMap, bool) ///< If true, no tractogram but a probability map is created as output. itkSetMacro( StopTracking, bool ) - itkSetMacro( InterpolateMasks, bool ) - itkSetMacro( TrialsPerSeed, unsigned int ) ///< When using probabilistic tractography, each seed point is used N times until a valid streamline that is compliant with all thresholds etc. is found - itkSetMacro( TrackingPriorWeight, float) ///< Weight between prior and data [0-1]. One mean tracking only on the prior peaks, zero only on the data. - itkSetMacro( TrackingPriorAsMask, bool) ///< If true, data directions in voxels where prior directions are invalid are set to zero - itkSetMacro( IntroduceDirectionsFromPrior, bool) ///< If false, prior voxels with invalid data voxel are ignored ///< Use manually defined points in physical space as seed points instead of seed image void SetSeedPoints( const std::vector< itk::Point >& sP) { m_SeedPoints = sP; } void SetTrackingHandler( mitk::TrackingDataHandler* h ) ///< { m_TrackingHandler = h; } void Update() override{ this->GenerateData(); } std::string GetStatusText(); void SetTrackingPriorHandler(mitk::TrackingDataHandler *TrackingPriorHandler); + std::shared_ptr< mitk::StreamlineTractographyParameters > GetParameters() const; + void SetParameters(std::shared_ptr< mitk::StreamlineTractographyParameters > Parameters); + protected: void GenerateData() override; StreamlineTrackingFilter(); ~StreamlineTrackingFilter() override {} bool IsValidFiber(FiberType* fib); ///< Check endpoints void FiberToProbmap(FiberType* fib); void GetSeedPointsFromSeedImage(); void CalculateNewPosition(itk::Point& pos, vnl_vector_fixed& dir); ///< Calculate next integration step. float FollowStreamline(itk::Point start_pos, vnl_vector_fixed dir, FiberType* fib, DirectionContainer* container, float tractLength, bool front, bool& exclude); ///< Start streamline in one direction. vnl_vector_fixed GetNewDirection(const itk::Point& pos, std::deque< vnl_vector_fixed >& olddirs, itk::Index<3>& oldIndex); ///< Determine new direction by sample voting at the current position taking the last progression direction into account. std::vector< vnl_vector_fixed > CreateDirections(unsigned int NPoints); void BeforeTracking(); void AfterTracking(); PolyDataType m_FiberPolyData; vtkSmartPointer m_Points; vtkSmartPointer m_Cells; BundleType m_Tractogram; BundleType m_GmStubs; ItkFloatImgType::Pointer m_StoppingRegions; ItkFloatImgType::Pointer m_TargetRegions; ItkFloatImgType::Pointer m_SeedImage; ItkFloatImgType::Pointer m_MaskImage; ItkFloatImgType::Pointer m_ExclusionRegions; ItkDoubleImgType::Pointer m_OutputProbabilityMap; - float m_MinVoxelSize; - float m_AngularThresholdDeg; - float m_StepSizeVox; - float m_SamplingDistanceVox; - float m_AngularThreshold; - float m_StepSize; - int m_MaxLength; - float m_MinTractLength; - float m_MaxTractLength; - int m_SeedsPerVoxel; - - bool m_AvoidStop; - bool m_RandomSampling; - float m_SamplingDistance; - float m_DeflectionMod; - bool m_OnlyForwardSamples; - bool m_UseStopVotes; - unsigned int m_NumberOfSamples; - - unsigned int m_NumPreviousDirections; - int m_MaxNumTracts; - bool m_Verbose; - float m_LoopCheck; bool m_DemoMode; - bool m_Random; - bool m_UseOutputProbabilityMap; std::vector< itk::Point > m_SeedPoints; unsigned int m_CurrentTracts; unsigned int m_Progress; bool m_StopTracking; - bool m_InterpolateMasks; - unsigned int m_TrialsPerSeed; - EndpointConstraints m_EndpointConstraint; void BuildFibers(bool check); float CheckCurvature(DirectionContainer *fib, bool front); // decision forest mitk::TrackingDataHandler* m_TrackingHandler; std::vector< PolyDataType > m_PolyDataContainer; std::chrono::time_point m_StartTime; std::chrono::time_point m_EndTime; itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::Pointer m_MaskInterpolator; itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::Pointer m_StopInterpolator; itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::Pointer m_TargetInterpolator; itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::Pointer m_SeedInterpolator; itk::LinearInterpolateImageFunction< ItkFloatImgType, float >::Pointer m_ExclusionInterpolator; bool m_SeedImageSet; bool m_TargetImageSet; + std::shared_ptr< mitk::StreamlineTractographyParameters > m_Parameters; + // Directional priors - bool m_IntroduceDirectionsFromPrior; - bool m_TrackingPriorAsMask; - float m_TrackingPriorWeight; mitk::TrackingDataHandler* m_TrackingPriorHandler; private: }; } //#ifndef ITK_MANUAL_INSTANTIATION //#include "itkMLBSTrackingFilter.cpp" //#endif #endif //__itkMLBSTrackingFilter_h_ diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/Sequences/mitkFastSpinEcho.h b/Modules/DiffusionImaging/FiberTracking/Fiberfox/Sequences/mitkFastSpinEcho.h index 399537366d..e631b1ab2e 100644 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/Sequences/mitkFastSpinEcho.h +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/Sequences/mitkFastSpinEcho.h @@ -1,94 +1,96 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_FastSpinEcho_H #define _MITK_FastSpinEcho_H #include namespace mitk { /** * \brief Fast spin echo sequence. Cartesian readout. * Echo spacing = TE */ class FastSpinEcho : public AcquisitionType { public: unsigned int linesWithSameTime; float half_read_time; FastSpinEcho(FiberfoxParameters* parameters) : AcquisitionType(parameters) { linesWithSameTime = static_cast(std::ceil(static_cast(kyMax)/m_Parameters->m_SignalGen.m_EchoTrainLength)); dt = m_Parameters->m_SignalGen.m_tLine/kxMax; // time to read one k-space voxel half_read_time = kxMax * dt/2; } ~FastSpinEcho() override {} // one echo per k-space line float GetTimeFromMaxEcho(const int& tick) override { float t = dt*(tick % kxMax + 0.5f) - half_read_time; return t; } float GetTimeFromLastDiffusionGradient(const int& tick) override { return (tick % kxMax)*dt; } // depends on ETL float GetTimeFromRf(const int& tick) override { - return m_Parameters->m_SignalGen.m_tEcho*std::ceil(static_cast(tick/kxMax+1)/linesWithSameTime) + GetTimeFromMaxEcho(tick); + float echo_nr = std::ceil(static_cast(tick/kxMax+1)/linesWithSameTime); + echo_nr -= std::ceil(static_cast(m_Parameters->m_SignalGen.m_EchoTrainLength)/2); + return m_Parameters->m_SignalGen.m_tEcho + echo_nr*m_Parameters->m_SignalGen.m_tLine + GetTimeFromMaxEcho(tick); } itk::Index< 2 > GetActualKspaceIndex(const int& tick) override { itk::Index< 2 > out_idx; out_idx[0] = tick % kxMax; out_idx[1] = tick / kxMax; // reverse phase if (!m_Parameters->m_SignalGen.m_ReversePhase) out_idx[1] = kyMax-1-out_idx[1]; return out_idx; } void AdjustEchoTime() override { - if ( m_Parameters->m_SignalGen.m_tEcho < m_Parameters->m_SignalGen.m_tLine ) + if ( m_Parameters->m_SignalGen.m_tEcho < m_Parameters->m_SignalGen.m_EchoTrainLength*m_Parameters->m_SignalGen.m_tLine ) { - m_Parameters->m_SignalGen.m_tEcho = m_Parameters->m_SignalGen.m_tLine; + m_Parameters->m_SignalGen.m_tEcho = m_Parameters->m_SignalGen.m_EchoTrainLength*m_Parameters->m_SignalGen.m_tLine; MITK_WARN << "Echo time is too short! Time not sufficient to read slice. Automatically adjusted to " << m_Parameters->m_SignalGen.m_tEcho << " ms"; m_Parameters->m_Misc.m_AfterSimulationMessage += "Echo time was chosen too short! Time not sufficient to read slice. Internally adjusted to " + boost::lexical_cast(m_Parameters->m_SignalGen.m_tEcho) + " ms\n"; } } protected: }; } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/Sequences/mitkSingleShotEpi.h b/Modules/DiffusionImaging/FiberTracking/Fiberfox/Sequences/mitkSingleShotEpi.h index 7c33a381d0..2203da4314 100644 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/Sequences/mitkSingleShotEpi.h +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/Sequences/mitkSingleShotEpi.h @@ -1,104 +1,102 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_SingleShotEpi_H #define _MITK_SingleShotEpi_H #include namespace mitk { /** * \brief Realizes EPI readout: one echo, maximum intensity in the k-space center, zig-zag trajectory * */ class SingleShotEpi : public AcquisitionType { public: /* TE | dt | dt | dt | ... | Total read time: Nvox*dt = kxMax*kyMax*dt */ float half_read_time; SingleShotEpi(FiberfoxParameters* parameters) : AcquisitionType(parameters) { dt = m_Parameters->m_SignalGen.m_tLine/kxMax; // time to read one k-space voxel half_read_time = (kxMax*kyMax) * dt/2; } ~SingleShotEpi() override {} // one echo per slice float GetTimeFromMaxEcho(const int& tick) override { float t = dt*(static_cast(tick) + 0.5f) - half_read_time; return t; } // we simply assume that readout starts directly after the last diffusion gradient float GetTimeFromLastDiffusionGradient(const int& tick) override { return tick*dt + dt/2; } float GetTimeFromRf(const int& tick) override { return m_Parameters->m_SignalGen.m_tEcho + GetTimeFromMaxEcho(tick); } itk::Index< 2 > GetActualKspaceIndex(const int& tick) override { itk::Index< 2 > out_idx; out_idx[0] = tick % kxMax; out_idx[1] = tick / kxMax; if (!m_Parameters->m_SignalGen.m_ReversePhase) { out_idx[1] = kyMax-1-out_idx[1]; // in the not reversed case we start at the maximum k-space line if (out_idx[1]%2 == 1) // reverse frequency encoding direction out_idx[0] = kxMax-out_idx[0]-1; } else if (out_idx[1]%2) // reverse frequency encoding direction out_idx[0] = kxMax-out_idx[0]-1; return out_idx; } void AdjustEchoTime() override { - auto temp = m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(1)*m_Parameters->m_SignalGen.m_PartialFourier - (m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(1)+m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(1)%2)/2; - - if ( m_Parameters->m_SignalGen.m_tEcho/2 < temp*m_Parameters->m_SignalGen.m_tLine ) + if ( m_Parameters->m_SignalGen.m_tEcho < kyMax*m_Parameters->m_SignalGen.m_tLine ) { - m_Parameters->m_SignalGen.m_tEcho = 2*temp*m_Parameters->m_SignalGen.m_tLine; + m_Parameters->m_SignalGen.m_tEcho = kyMax*m_Parameters->m_SignalGen.m_tLine; MITK_WARN << "Echo time is too short! Time not sufficient to read slice. Automatically adjusted to " << m_Parameters->m_SignalGen.m_tEcho << " ms"; m_Parameters->m_Misc.m_AfterSimulationMessage += "Echo time was chosen too short! Time not sufficient to read slice. Internally adjusted to " + boost::lexical_cast(m_Parameters->m_SignalGen.m_tEcho) + " ms\n"; } } protected: }; } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.cpp index 4ebef7a526..b736e30811 100644 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.cpp @@ -1,498 +1,512 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __itkKspaceImageFilter_txx #define __itkKspaceImageFilter_txx //#endif #include #include #include #include #include "itkKspaceImageFilter.h" #include #include #include #include #include #include #include #include namespace itk { template< class ScalarType > KspaceImageFilter< ScalarType >::KspaceImageFilter() : m_Z(0) , m_RandSeed(-1) , m_SpikesPerSlice(0) , m_IsBaseline(true) + , m_StoreTimings(false) { m_DiffusionGradientDirection.Fill(0.0); m_CoilPosition.Fill(0.0); } template< class ScalarType > void KspaceImageFilter< ScalarType > ::BeforeThreadedGenerateData() { m_Spike = vcl_complex(0,0); m_SpikeLog = ""; m_TransX = -m_Translation[0]; m_TransY = -m_Translation[1]; m_TransZ = -m_Translation[2]; kxMax = m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(0); kyMax = m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(1); xMax = m_CompartmentImages.at(0)->GetLargestPossibleRegion().GetSize(0); // scanner coverage in x-direction yMax = m_CompartmentImages.at(0)->GetLargestPossibleRegion().GetSize(1); // scanner coverage in y-direction yMaxFov = yMax; if (m_Parameters->m_Misc.m_DoAddAliasing) { // actual FOV in y-direction (in x-direction FOV=xMax) yMaxFov = static_cast(yMaxFov * m_Parameters->m_SignalGen.m_CroppingFactor); } yMaxFov_half = (yMaxFov-1)/2; numPix = kxMax*kyMax; float ringing_factor = static_cast(m_Parameters->m_SignalGen.m_ZeroRinging)/100.0; ringing_lines_x = static_cast(ceil(kxMax/2 * ringing_factor)); ringing_lines_y = static_cast(ceil(kyMax/2 * ringing_factor)); // Adjust noise variance since it is the intended variance in physical space and not in k-space: float noiseVar = m_Parameters->m_SignalGen.m_PartialFourier*m_Parameters->m_SignalGen.m_NoiseVariance/(kyMax*kxMax); m_RandGen = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); if (m_RandSeed>=0) // always generate the same random numbers? m_RandGen->SetSeed(m_RandSeed); else m_RandGen->SetSeed(); typename OutputImageType::Pointer outputImage = OutputImageType::New(); itk::ImageRegion<2> region; region.SetSize(0, m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(0)); region.SetSize(1, m_Parameters->m_SignalGen.m_CroppedRegion.GetSize(1)); outputImage->SetLargestPossibleRegion( region ); outputImage->SetBufferedRegion( region ); outputImage->SetRequestedRegion( region ); outputImage->Allocate(); vcl_complex zero = vcl_complex(0, 0); outputImage->FillBuffer(zero); if (m_Parameters->m_SignalGen.m_NoiseVariance>0 && m_Parameters->m_Misc.m_DoAddNoise) { ImageRegionIterator< OutputImageType > oit(outputImage, outputImage->GetLargestPossibleRegion()); while( !oit.IsAtEnd() ) { oit.Set(vcl_complex(m_RandGen->GetNormalVariate(0, noiseVar), m_RandGen->GetNormalVariate(0, noiseVar))); ++oit; } } m_KSpaceImage = InputImageType::New(); m_KSpaceImage->SetLargestPossibleRegion( region ); m_KSpaceImage->SetBufferedRegion( region ); m_KSpaceImage->SetRequestedRegion( region ); m_KSpaceImage->Allocate(); m_KSpaceImage->FillBuffer(0.0); -// m_TickImage = InputImageType::New(); -// m_TickImage->SetLargestPossibleRegion( region ); -// m_TickImage->SetBufferedRegion( region ); -// m_TickImage->SetRequestedRegion( region ); -// m_TickImage->Allocate(); -// m_TickImage->FillBuffer(-1.0); + if (m_StoreTimings) + { + m_TickImage = InputImageType::New(); + m_TickImage->SetLargestPossibleRegion( region ); + m_TickImage->SetBufferedRegion( region ); + m_TickImage->SetRequestedRegion( region ); + m_TickImage->Allocate(); + m_TickImage->FillBuffer(-1.0); + + m_RfImage = InputImageType::New(); + m_RfImage->SetLargestPossibleRegion( region ); + m_RfImage->SetBufferedRegion( region ); + m_RfImage->SetRequestedRegion( region ); + m_RfImage->Allocate(); + m_RfImage->FillBuffer(-1.0); + } + else + { + m_TickImage = nullptr; + m_RfImage = nullptr; + } m_Gamma = 42576000*itk::Math::twopi; // Gyromagnetic ratio in Hz/T if ( m_Parameters->m_SignalGen.m_EddyStrength>0 && m_DiffusionGradientDirection.GetNorm()>0.001) { m_DiffusionGradientDirection = m_DiffusionGradientDirection * m_Parameters->m_SignalGen.m_EddyStrength/1000 * m_Gamma; m_IsBaseline = false; } else { m_IsBaseline = true; } this->SetNthOutput(0, outputImage); for (int i=0; i<3; i++) for (int j=0; j<3; j++) m_Transform[i][j] = m_Parameters->m_SignalGen.m_ImageDirection[i][j] * m_Parameters->m_SignalGen.m_ImageSpacing[j]/1000; float a = m_Parameters->m_SignalGen.m_ImageRegion.GetSize(0)*m_Parameters->m_SignalGen.m_ImageSpacing[0]; float b = m_Parameters->m_SignalGen.m_ImageRegion.GetSize(1)*m_Parameters->m_SignalGen.m_ImageSpacing[1]; float diagonal = sqrt(a*a+b*b)/1000; // image diagonal in m switch (m_Parameters->m_SignalGen.m_CoilSensitivityProfile) { case SignalGenerationParameters::COIL_CONSTANT: { m_CoilSensitivityFactor = 1; // same signal everywhere break; } case SignalGenerationParameters::COIL_LINEAR: { m_CoilSensitivityFactor = -1/diagonal; // about 50% of the signal in the image center remaining break; } case SignalGenerationParameters::COIL_EXPONENTIAL: { m_CoilSensitivityFactor = -log(0.1)/diagonal; // about 32% of the signal in the image center remaining break; } } switch (m_Parameters->m_SignalGen.m_AcquisitionType) { case SignalGenerationParameters::SingleShotEpi: m_ReadoutScheme = new mitk::SingleShotEpi(m_Parameters); break; case SignalGenerationParameters::ConventionalSpinEcho: m_ReadoutScheme = new mitk::ConventionalSpinEcho(m_Parameters); break; case SignalGenerationParameters::FastSpinEcho: m_ReadoutScheme = new mitk::FastSpinEcho(m_Parameters); break; default: m_ReadoutScheme = new mitk::SingleShotEpi(m_Parameters); } m_ReadoutScheme->AdjustEchoTime(); m_MovedFmap = nullptr; if (m_Parameters->m_Misc.m_DoAddDistortions && m_Parameters->m_SignalGen.m_FrequencyMap.IsNotNull() && m_Parameters->m_SignalGen.m_DoAddMotion) { // we have to account for the head motion since this also moves our frequency map itk::LinearInterpolateImageFunction< itk::Image< float, 3 >, float >::Pointer fmapInterpolator; fmapInterpolator = itk::LinearInterpolateImageFunction< itk::Image< float, 3 >, float >::New(); fmapInterpolator->SetInputImage(m_Parameters->m_SignalGen.m_FrequencyMap); m_MovedFmap = itk::Image< ScalarType, 2 >::New(); m_MovedFmap->SetLargestPossibleRegion( m_CompartmentImages.at(0)->GetLargestPossibleRegion() ); m_MovedFmap->SetBufferedRegion( m_CompartmentImages.at(0)->GetLargestPossibleRegion() ); m_MovedFmap->SetRequestedRegion( m_CompartmentImages.at(0)->GetLargestPossibleRegion() ); m_MovedFmap->Allocate(); m_MovedFmap->FillBuffer(0); ImageRegionIterator< InputImageType > it(m_MovedFmap, m_MovedFmap->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { itk::Image::IndexType index; index[0] = it.GetIndex()[0]; index[1] = it.GetIndex()[1]; index[2] = m_Zidx; itk::Point point3D; m_Parameters->m_SignalGen.m_FrequencyMap->TransformIndexToPhysicalPoint(index, point3D); m_FiberBundle->TransformPoint( point3D, m_RotationMatrix, m_TransX, m_TransY, m_TransZ ); it.Set(mitk::imv::GetImageValue(point3D, true, fmapInterpolator)); ++it; } } // calculate T1 relaxation (independent of actual readout) m_T1Relax.clear(); if ( m_Parameters->m_SignalGen.m_DoSimulateRelaxation) for (unsigned int i=0; im_SignalGen.m_tRep/m_T1[i])); // account for inversion pulse and TI if (m_Parameters->m_SignalGen.m_tInv > 0) relaxation *= (1.0-std::exp(std::log(2) - m_Parameters->m_SignalGen.m_tInv/m_T1[i])); m_T1Relax.push_back(relaxation); } } template< class ScalarType > float KspaceImageFilter< ScalarType >::CoilSensitivity(VectorType& pos) { // ************************************************************************* // Coil ring is moving with excited slice (FIX THIS SOMETIME) m_CoilPosition[2] = pos[2]; // ************************************************************************* switch (m_Parameters->m_SignalGen.m_CoilSensitivityProfile) { case SignalGenerationParameters::COIL_CONSTANT: return 1; case SignalGenerationParameters::COIL_LINEAR: { VectorType diff = pos-m_CoilPosition; float sens = diff.GetNorm()*m_CoilSensitivityFactor + 1; if (sens<0) sens = 0; return sens; } case SignalGenerationParameters::COIL_EXPONENTIAL: { VectorType diff = pos-m_CoilPosition; float dist = static_cast(diff.GetNorm()); return std::exp(-dist*m_CoilSensitivityFactor); } default: return 1; } } template< class ScalarType > void KspaceImageFilter< ScalarType > ::ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, ThreadIdType ) { typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); ImageRegionIterator< OutputImageType > oit(outputImage, outputRegionForThread); typedef ImageRegionConstIterator< InputImageType > InputIteratorType; // precalculate shifts for DFT float x_shift = 0; float y_shift = 0; if (static_cast(xMax)%2==1) x_shift = (xMax-1)/2; else x_shift = xMax/2; if (static_cast(yMax)%2==1) y_shift = (yMax-1)/2; else y_shift = yMax/2; float kx_shift = 0; float ky_shift = 0; if (static_cast(kxMax)%2==1) kx_shift = (kxMax-1)/2; else kx_shift = kxMax/2; if (static_cast(kyMax)%2==1) ky_shift = (kyMax-1)/2; else ky_shift = kyMax/2; vcl_complex zero = vcl_complex(0, 0); while( !oit.IsAtEnd() ) { int tick = oit.GetIndex()[1] * kxMax + oit.GetIndex()[0]; // get current k-space index (depends on the chosen k-space readout scheme) itk::Index< 2 > kIdx = m_ReadoutScheme->GetActualKspaceIndex(tick); // we have to adjust the ticks to obtain correct times since the DFT is not completely symmetric in the even number of lines case if (static_cast(kyMax)%2 == 0 && !m_Parameters->m_SignalGen.m_ReversePhase) { tick += kxMax; tick %= static_cast(numPix); } // partial fourier // two cases because we always want to skip the "later" parts of k-space // in "normal" phase direction, the higher k-space indices are acquired first // in reversed phase direction, the higher k-space indices are acquired later // if the image has an even number of lines, never skip line zero since it is missing on the other side (DFT not completely syymetric in even case) if ((m_Parameters->m_SignalGen.m_ReversePhase && kIdx[1]>std::ceil(kyMax*m_Parameters->m_SignalGen.m_PartialFourier)) || (!m_Parameters->m_SignalGen.m_ReversePhase && kIdx[1]m_SignalGen.m_PartialFourier)) && (kIdx[1]>0 || static_cast(kyMax)%2 == 1))) { outputImage->SetPixel(kIdx, zero); ++oit; continue; } -// m_TickImage->SetPixel(kIdx, tick); + if (m_StoreTimings) + m_TickImage->SetPixel(kIdx, tick); // gibbs ringing by setting high frequencies to zero (alternative to using smaller k-space than input image space) if (m_Parameters->m_SignalGen.m_DoAddGibbsRinging && m_Parameters->m_SignalGen.m_ZeroRinging>0) { if (kIdx[0] < ringing_lines_x || kIdx[1] < ringing_lines_y || kIdx[0] >= kxMax - ringing_lines_x || kIdx[1] >= kyMax - ringing_lines_y) { outputImage->SetPixel(kIdx, zero); ++oit; continue; } } // time from maximum echo float t = m_ReadoutScheme->GetTimeFromMaxEcho(tick); // calculate eddy current decay factor float eddyDecay = 0; if ( m_Parameters->m_Misc.m_DoAddEddyCurrents && m_Parameters->m_SignalGen.m_EddyStrength>0 && !m_IsBaseline) { // time passed since k-space readout started float tRead = m_ReadoutScheme->GetTimeFromLastDiffusionGradient(tick); eddyDecay = std::exp(-tRead/m_Parameters->m_SignalGen.m_Tau ) * t/1000; // time in seconds here } // calcualte signal relaxation factors std::vector< float > relaxFactor; if ( m_Parameters->m_SignalGen.m_DoSimulateRelaxation) { // time passes since application of the RF pulse float tRf = m_ReadoutScheme->GetTimeFromRf(tick); + if (m_StoreTimings) + m_RfImage->SetPixel(kIdx, tRf); for (unsigned int i=0; im_SignalGen.m_tInhom)); } } // shift k for DFT: (0 -- N) --> (-N/2 -- N/2) float kx = kIdx[0] - kx_shift; float ky = kIdx[1] - ky_shift; // add ghosting by adding gradient delay induced offset if (m_Parameters->m_Misc.m_DoAddGhosts) { if (kIdx[1]%2 == 1) kx -= m_Parameters->m_SignalGen.m_KspaceLineOffset; else kx += m_Parameters->m_SignalGen.m_KspaceLineOffset; } // pull stuff out of inner loop t /= 1000; // time in seconds kx /= xMax; ky /= yMaxFov; // calculate signal s at k-space position (kx, ky) vcl_complex s(0,0); InputIteratorType it(m_CompartmentImages[0], m_CompartmentImages[0]->GetLargestPossibleRegion() ); while( !it.IsAtEnd() ) { typename InputImageType::IndexType input_idx = it.GetIndex(); // shift x,y for DFT: (0 -- N) --> (-N/2 -- N/2) float x = input_idx[0] - x_shift; float y = input_idx[1] - y_shift; // sum compartment signals and simulate relaxation ScalarType f_real = 0; for (unsigned int i=0; im_SignalGen.m_DoSimulateRelaxation) f_real += m_CompartmentImages[i]->GetPixel(input_idx) * relaxFactor[i]; else f_real += m_CompartmentImages[i]->GetPixel(input_idx); // vector from image center to current position (in meter) // only necessary for eddy currents and non-constant coil sensitivity VectorType pos; if ((m_Parameters->m_Misc.m_DoAddEddyCurrents && m_Parameters->m_SignalGen.m_EddyStrength>0 && !m_IsBaseline) || m_Parameters->m_SignalGen.m_CoilSensitivityProfile!=SignalGenerationParameters::COIL_CONSTANT) { pos[0] = x; pos[1] = y; pos[2] = m_Z; pos = m_Transform*pos; } if (m_Parameters->m_SignalGen.m_CoilSensitivityProfile!=SignalGenerationParameters::COIL_CONSTANT) f_real *= CoilSensitivity(pos); // simulate eddy currents and other distortions float phi = 0; // phase shift if ( m_Parameters->m_Misc.m_DoAddEddyCurrents && m_Parameters->m_SignalGen.m_EddyStrength>0 && !m_IsBaseline) { // duration (tRead) already included in "eddyDecay" phi += (m_DiffusionGradientDirection[0]*pos[0]+m_DiffusionGradientDirection[1]*pos[1]+m_DiffusionGradientDirection[2]*pos[2]) * eddyDecay; } // simulate distortions if (m_Parameters->m_Misc.m_DoAddDistortions) { if (m_MovedFmap.IsNotNull()) // if we have headmotion, use moved map phi += m_MovedFmap->GetPixel(input_idx) * t; else if (m_Parameters->m_SignalGen.m_FrequencyMap.IsNotNull()) { itk::Image::IndexType index; index[0] = input_idx[0]; index[1] = input_idx[1]; index[2] = m_Zidx; phi += m_Parameters->m_SignalGen.m_FrequencyMap->GetPixel(index) * t; } } // if signal comes from outside FOV, mirror it back (wrap-around artifact - aliasing if (m_Parameters->m_Misc.m_DoAddAliasing) { if (y<-yMaxFov_half) y += yMaxFov; else if (y>yMaxFov_half) y -= yMaxFov; } // actual DFT term vcl_complex f(f_real * m_Parameters->m_SignalGen.m_SignalScale, 0); s += f * std::exp( std::complex(0, itk::Math::twopi * (kx*x + ky*y + phi )) ); ++it; } s /= numPix; if (m_SpikesPerSlice>0 && sqrt(s.imag()*s.imag()+s.real()*s.real()) > sqrt(m_Spike.imag()*m_Spike.imag()+m_Spike.real()*m_Spike.real()) ) m_Spike = s; s += outputImage->GetPixel(kIdx); // add precalculated noise outputImage->SetPixel(kIdx, s); m_KSpaceImage->SetPixel(kIdx, sqrt(s.imag()*s.imag()+s.real()*s.real()) ); ++oit; } } template< class ScalarType > void KspaceImageFilter< ScalarType > ::AfterThreadedGenerateData() { typename OutputImageType::Pointer outputImage = static_cast< OutputImageType * >(this->ProcessObject::GetOutput(0)); int kxMax = outputImage->GetLargestPossibleRegion().GetSize(0); // k-space size in x-direction int kyMax = outputImage->GetLargestPossibleRegion().GetSize(1); // k-space size in y-direction ImageRegionIterator< OutputImageType > oit(outputImage, outputImage->GetLargestPossibleRegion()); while( !oit.IsAtEnd() ) // use hermitian k-space symmetry to fill empty k-space parts resulting from partial fourier acquisition { int tick = oit.GetIndex()[1] * kxMax + oit.GetIndex()[0]; auto kIdx = m_ReadoutScheme->GetActualKspaceIndex(tick); if ((m_Parameters->m_SignalGen.m_ReversePhase && kIdx[1]>std::ceil(kyMax*m_Parameters->m_SignalGen.m_PartialFourier)) || (!m_Parameters->m_SignalGen.m_ReversePhase && kIdx[1]m_SignalGen.m_PartialFourier)) && (kIdx[1]>0 || static_cast(kyMax)%2 == 1))) { // calculate symmetric index auto sym = m_ReadoutScheme->GetSymmetricIndex(kIdx); // use complex conjugate of symmetric index value at current index vcl_complex s = outputImage->GetPixel(sym); s = vcl_complex(s.real(), -s.imag()); outputImage->SetPixel(kIdx, s); m_KSpaceImage->SetPixel(kIdx, sqrt(s.imag()*s.imag()+s.real()*s.real()) ); } ++oit; } m_Spike *= m_Parameters->m_SignalGen.m_SpikeAmplitude; itk::Index< 2 > spikeIdx; for (unsigned int i=0; iGetIntegerVariate()%kxMax; spikeIdx[1] = m_RandGen->GetIntegerVariate()%kyMax; outputImage->SetPixel(spikeIdx, m_Spike); m_SpikeLog += "[" + boost::lexical_cast(spikeIdx[0]) + "," + boost::lexical_cast(spikeIdx[1]) + "," + boost::lexical_cast(m_Zidx) + "] Magnitude: " + boost::lexical_cast(m_Spike.real()) + "+" + boost::lexical_cast(m_Spike.imag()) + "i\n"; } delete m_ReadoutScheme; - -// typename itk::ImageFileWriter< InputImageType >::Pointer wr = itk::ImageFileWriter< InputImageType >::New(); -// wr->SetInput(m_TickImage); -// wr->SetFileName("/home/neher/TimeFromRfImage.nii.gz"); -// wr->Update(); } } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.h b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.h index 132d92b52f..471eec3723 100644 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.h +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkKspaceImageFilter.h @@ -1,156 +1,161 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef __itkKspaceImageFilter_h_ #define __itkKspaceImageFilter_h_ #include #include #include #include #include #include #include #include namespace itk{ /** * \brief Simulates k-space acquisition of one slice with a single shot EPI sequence. Enables the simulation of various effects occuring during real MR acquisitions: * - T2 signal relaxation * - Spikes * - N/2 Ghosts * - Aliasing (wrap around) * - Image distortions (off-frequency effects) * - Gibbs ringing * - Eddy current effects * Based on a discrete fourier transformation. * See "Fiberfox: Facilitating the creation of realistic white matter software phantoms" (DOI: 10.1002/mrm.25045) for details. */ template< class ScalarType > class KspaceImageFilter : public ImageSource< Image< vcl_complex< ScalarType >, 2 > > { public: typedef KspaceImageFilter Self; typedef SmartPointer Pointer; typedef SmartPointer ConstPointer; typedef ImageSource< Image< vcl_complex< ScalarType >, 2 > > Superclass; /** Method for creation through the object factory. */ itkFactorylessNewMacro(Self) itkCloneMacro(Self) /** Runtime information support. */ itkTypeMacro(KspaceImageFilter, ImageToImageFilter) typedef typename itk::Image< ScalarType, 2 > InputImageType; typedef typename InputImageType::Pointer InputImagePointerType; typedef typename Superclass::OutputImageType OutputImageType; typedef typename Superclass::OutputImageRegionType OutputImageRegionType; typedef itk::Matrix MatrixType; typedef itk::Point Point2D; typedef itk::Vector< float,3> VectorType; itkSetMacro( SpikesPerSlice, unsigned int ) ///< Number of spikes per slice. Corresponding parameter in fiberfox parameter object specifies the number of spikes for the whole image and can thus not be used here. itkSetMacro( Z, double ) ///< Slice position, necessary for eddy current simulation. itkSetMacro( RandSeed, int ) ///< Use constant seed for random generator for reproducible results. itkSetMacro( Translation, VectorType ) itkSetMacro( RotationMatrix, MatrixType ) itkSetMacro( Zidx, int ) + itkSetMacro( StoreTimings, bool ) itkSetMacro( FiberBundle, FiberBundle::Pointer ) itkSetMacro( CoilPosition, VectorType ) itkGetMacro( KSpaceImage, typename InputImageType::Pointer ) ///< k-space magnitude image + itkGetMacro( TickImage, typename InputImageType::Pointer ) ///< k-space readout ordering encoded in the voxels + itkGetMacro( RfImage, typename InputImageType::Pointer ) ///< time passed since last RF pulse encoded per voxel itkGetMacro( SpikeLog, std::string ) void SetParameters( FiberfoxParameters* param ){ m_Parameters = param; } void SetCompartmentImages( std::vector< InputImagePointerType > cImgs ) { m_CompartmentImages=cImgs; } ///< One signal image per compartment. void SetT2( std::vector< float > t2Vector ) { m_T2=t2Vector; } ///< One T2 relaxation constant per compartment image. void SetT1( std::vector< float > t1Vector ) { m_T1=t1Vector; } ///< One T1 relaxation constant per compartment image. void SetDiffusionGradientDirection(itk::Vector g) { m_DiffusionGradientDirection=g; } ///< Gradient direction is needed for eddy current simulation. protected: KspaceImageFilter(); ~KspaceImageFilter() override {} float CoilSensitivity(VectorType& pos); void BeforeThreadedGenerateData() override; void ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, ThreadIdType threadID) override; void AfterThreadedGenerateData() override; VectorType m_CoilPosition; FiberfoxParameters* m_Parameters; std::vector< float > m_T2; std::vector< float > m_T1; std::vector< float > m_T1Relax; std::vector< InputImagePointerType > m_CompartmentImages; itk::Vector m_DiffusionGradientDirection; float m_Z; int m_Zidx; int m_RandSeed; itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer m_RandGen; unsigned int m_SpikesPerSlice; FiberBundle::Pointer m_FiberBundle; float m_Gamma; VectorType m_Translation; ///< used to find correct point in frequency map (head motion) MatrixType m_RotationMatrix; float m_TransX; float m_TransY; float m_TransZ; bool m_IsBaseline; vcl_complex m_Spike; MatrixType m_Transform; std::string m_SpikeLog; float m_CoilSensitivityFactor; typename InputImageType::Pointer m_KSpaceImage; typename InputImageType::Pointer m_TickImage; + typename InputImageType::Pointer m_RfImage; AcquisitionType* m_ReadoutScheme; typename itk::Image< ScalarType, 2 >::Pointer m_MovedFmap; int ringing_lines_x; int ringing_lines_y; float kxMax; float kyMax; float xMax; float yMax; float yMaxFov; float yMaxFov_half; float numPix; + bool m_StoreTimings; private: }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkKspaceImageFilter.cpp" #endif #endif //__itkKspaceImageFilter_h_ diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp index 46db14c13f..f2e8215b2c 100755 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.cpp @@ -1,1750 +1,1765 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "itkTractsToDWIImageFilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace itk { template< class PixelType > TractsToDWIImageFilter< PixelType >::TractsToDWIImageFilter() : m_StatusText("") , m_UseConstantRandSeed(false) , m_RandGen(itk::Statistics::MersenneTwisterRandomVariateGenerator::New()) { m_DoubleInterpolator = itk::LinearInterpolateImageFunction< ItkDoubleImgType, float >::New(); m_NullDir.Fill(0); } template< class PixelType > TractsToDWIImageFilter< PixelType >::~TractsToDWIImageFilter() { } template< class PixelType > TractsToDWIImageFilter< PixelType >::DoubleDwiType::Pointer TractsToDWIImageFilter< PixelType >:: SimulateKspaceAcquisition( std::vector< DoubleDwiType::Pointer >& compartment_images ) { unsigned int numFiberCompartments = m_Parameters.m_FiberModelList.size(); // create slice object ImageRegion<2> sliceRegion; sliceRegion.SetSize(0, m_WorkingImageRegion.GetSize()[0]); sliceRegion.SetSize(1, m_WorkingImageRegion.GetSize()[1]); Vector< double, 2 > sliceSpacing; sliceSpacing[0] = m_WorkingSpacing[0]; sliceSpacing[1] = m_WorkingSpacing[1]; DoubleDwiType::PixelType nullPix; nullPix.SetSize(compartment_images.at(0)->GetVectorLength()); nullPix.Fill(0.0); auto magnitudeDwiImage = DoubleDwiType::New(); magnitudeDwiImage->SetSpacing( m_Parameters.m_SignalGen.m_ImageSpacing ); magnitudeDwiImage->SetOrigin( m_Parameters.m_SignalGen.m_ImageOrigin ); magnitudeDwiImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); magnitudeDwiImage->SetLargestPossibleRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); magnitudeDwiImage->SetBufferedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); magnitudeDwiImage->SetRequestedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); magnitudeDwiImage->SetVectorLength( compartment_images.at(0)->GetVectorLength() ); magnitudeDwiImage->Allocate(); magnitudeDwiImage->FillBuffer(nullPix); m_PhaseImage = DoubleDwiType::New(); m_PhaseImage->SetSpacing( m_Parameters.m_SignalGen.m_ImageSpacing ); m_PhaseImage->SetOrigin( m_Parameters.m_SignalGen.m_ImageOrigin ); m_PhaseImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); m_PhaseImage->SetLargestPossibleRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_PhaseImage->SetBufferedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_PhaseImage->SetRequestedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_PhaseImage->SetVectorLength( compartment_images.at(0)->GetVectorLength() ); m_PhaseImage->Allocate(); m_PhaseImage->FillBuffer(nullPix); m_KspaceImage = DoubleDwiType::New(); m_KspaceImage->SetSpacing( m_Parameters.m_SignalGen.m_ImageSpacing ); m_KspaceImage->SetOrigin( m_Parameters.m_SignalGen.m_ImageOrigin ); m_KspaceImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); m_KspaceImage->SetLargestPossibleRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_KspaceImage->SetBufferedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_KspaceImage->SetRequestedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_KspaceImage->SetVectorLength( m_Parameters.m_SignalGen.m_NumberOfCoils ); m_KspaceImage->Allocate(); m_KspaceImage->FillBuffer(nullPix); // calculate coil positions double a = m_Parameters.m_SignalGen.m_ImageRegion.GetSize(0)*m_Parameters.m_SignalGen.m_ImageSpacing[0]; double b = m_Parameters.m_SignalGen.m_ImageRegion.GetSize(1)*m_Parameters.m_SignalGen.m_ImageSpacing[1]; double c = m_Parameters.m_SignalGen.m_ImageRegion.GetSize(2)*m_Parameters.m_SignalGen.m_ImageSpacing[2]; double diagonal = sqrt(a*a+b*b)/1000; // image diagonal in m m_CoilPointset = mitk::PointSet::New(); std::vector< itk::Vector > coilPositions; itk::Vector pos; pos.Fill(0.0); pos[1] = -diagonal/2; itk::Vector center; center[0] = a/2-m_Parameters.m_SignalGen.m_ImageSpacing[0]/2; center[1] = b/2-m_Parameters.m_SignalGen.m_ImageSpacing[2]/2; center[2] = c/2-m_Parameters.m_SignalGen.m_ImageSpacing[1]/2; for (unsigned int c=0; cInsertPoint(c, pos*1000 + m_Parameters.m_SignalGen.m_ImageOrigin.GetVectorFromOrigin() + center ); double rz = 360.0/m_Parameters.m_SignalGen.m_NumberOfCoils * itk::Math::pi/180; vnl_matrix_fixed< double, 3, 3 > rotZ; rotZ.set_identity(); rotZ[0][0] = cos(rz); rotZ[1][1] = rotZ[0][0]; rotZ[0][1] = -sin(rz); rotZ[1][0] = -rotZ[0][1]; pos.SetVnlVector(rotZ*pos.GetVnlVector()); } auto num_slices = compartment_images.at(0)->GetLargestPossibleRegion().GetSize(2); auto num_gradient_volumes = static_cast(compartment_images.at(0)->GetVectorLength()); auto max_threads = omp_get_max_threads(); int out_threads = Math::ceil(std::sqrt(max_threads)); int in_threads = Math::floor(std::sqrt(max_threads)); if (out_threads > num_gradient_volumes) { out_threads = num_gradient_volumes; in_threads = Math::floor(static_cast(max_threads/out_threads)); } PrintToLog("Parallel volumes: " + boost::lexical_cast(out_threads), false, true, true); PrintToLog("Threads per slice: " + boost::lexical_cast(in_threads), false, true, true); std::list< std::tuple > spikes; if (m_Parameters.m_Misc.m_DoAddSpikes) for (unsigned int i=0; i( m_RandGen->GetIntegerVariate()%num_gradient_volumes, m_RandGen->GetIntegerVariate()%num_slices, m_RandGen->GetIntegerVariate()%m_Parameters.m_SignalGen.m_NumberOfCoils); spikes.push_back(spike); } + bool output_timing = m_Parameters.m_Misc.m_OutputAdditionalImages; + PrintToLog("0% 10 20 30 40 50 60 70 80 90 100%", false, true, false); PrintToLog("|----|----|----|----|----|----|----|----|----|----|\n*", false, false, false); unsigned long lastTick = 0; boost::progress_display disp(static_cast(num_gradient_volumes)*compartment_images.at(0)->GetLargestPossibleRegion().GetSize(2)); #pragma omp parallel for num_threads(out_threads) for (int g=0; gGetAbortGenerateData()) continue; std::list< std::tuple > spikeSlice; #pragma omp critical { for (auto spike : spikes) if (std::get<0>(spike) == static_cast(g)) spikeSlice.push_back(std::tuple(std::get<1>(spike), std::get<2>(spike))); } for (unsigned int z=0; z compartment_slices; std::vector< float > t2Vector; std::vector< float > t1Vector; for (unsigned int i=0; i* signalModel; if (iSetLargestPossibleRegion( sliceRegion ); slice->SetBufferedRegion( sliceRegion ); slice->SetRequestedRegion( sliceRegion ); slice->SetSpacing(sliceSpacing); slice->Allocate(); slice->FillBuffer(0.0); // extract slice from channel g for (unsigned int y=0; yGetLargestPossibleRegion().GetSize(1); y++) for (unsigned int x=0; xGetLargestPossibleRegion().GetSize(0); x++) { Float2DImageType::IndexType index2D; index2D[0]=x; index2D[1]=y; DoubleDwiType::IndexType index3D; index3D[0]=x; index3D[1]=y; index3D[2]=z; slice->SetPixel(index2D, compartment_images.at(i)->GetPixel(index3D)[g]); } compartment_slices.push_back(slice); t2Vector.push_back(signalModel->GetT2()); t1Vector.push_back(signalModel->GetT1()); } if (this->GetAbortGenerateData()) continue; for (unsigned int c=0; c(ss) == z && std::get<1>(ss) == c) ++numSpikes; // create k-sapce (inverse fourier transform slices) auto idft = itk::KspaceImageFilter< Float2DImageType::PixelType >::New(); idft->SetCompartmentImages(compartment_slices); idft->SetT2(t2Vector); idft->SetT1(t1Vector); if (m_UseConstantRandSeed) { int linear_seed = g + num_gradient_volumes*z + num_gradient_volumes*compartment_images.at(0)->GetLargestPossibleRegion().GetSize(2)*c; idft->SetRandSeed(linear_seed); } idft->SetParameters(&m_Parameters); idft->SetZ((float)z-(float)( compartment_images.at(0)->GetLargestPossibleRegion().GetSize(2) -compartment_images.at(0)->GetLargestPossibleRegion().GetSize(2)%2 ) / 2.0); idft->SetZidx(z); idft->SetCoilPosition(coilPositions.at(c)); idft->SetFiberBundle(m_FiberBundle); idft->SetTranslation(m_Translations.at(g)); idft->SetRotationMatrix(m_RotationsInv.at(g)); idft->SetDiffusionGradientDirection(m_Parameters.m_SignalGen.GetGradientDirection(g)*m_Parameters.m_SignalGen.GetBvalue()/1000.0); idft->SetSpikesPerSlice(numSpikes); idft->SetNumberOfThreads(in_threads); +#pragma omp critical + if (output_timing) + { + idft->SetStoreTimings(true); + output_timing = false; + } idft->Update(); #pragma omp critical if (numSpikes>0) { m_SpikeLog += "Volume " + boost::lexical_cast(g) + " Coil " + boost::lexical_cast(c) + "\n"; m_SpikeLog += idft->GetSpikeLog(); } Complex2DImageType::Pointer fSlice; fSlice = idft->GetOutput(); + if (idft->GetTickImage().IsNotNull()) + m_TickImage = idft->GetTickImage(); + if (idft->GetRfImage().IsNotNull()) + m_RfImage = idft->GetRfImage(); + // fourier transform slice Complex2DImageType::Pointer newSlice; auto dft = itk::DftImageFilter< Float2DImageType::PixelType >::New(); dft->SetInput(fSlice); dft->SetParameters(m_Parameters); dft->SetNumberOfThreads(in_threads); dft->Update(); newSlice = dft->GetOutput(); // put slice back into channel g for (unsigned int y=0; yGetLargestPossibleRegion().GetSize(1); y++) for (unsigned int x=0; xGetLargestPossibleRegion().GetSize(0); x++) { DoubleDwiType::IndexType index3D; index3D[0]=x; index3D[1]=y; index3D[2]=z; Complex2DImageType::IndexType index2D; index2D[0]=x; index2D[1]=y; Complex2DImageType::PixelType cPix = newSlice->GetPixel(index2D); double magn = sqrt(cPix.real()*cPix.real()+cPix.imag()*cPix.imag()); double phase = 0; if (cPix.real()!=0) phase = atan( cPix.imag()/cPix.real() ); DoubleDwiType::PixelType real_pix = m_OutputImagesReal.at(c)->GetPixel(index3D); real_pix[g] = cPix.real(); m_OutputImagesReal.at(c)->SetPixel(index3D, real_pix); DoubleDwiType::PixelType imag_pix = m_OutputImagesImag.at(c)->GetPixel(index3D); imag_pix[g] = cPix.imag(); m_OutputImagesImag.at(c)->SetPixel(index3D, imag_pix); DoubleDwiType::PixelType dwiPix = magnitudeDwiImage->GetPixel(index3D); DoubleDwiType::PixelType phasePix = m_PhaseImage->GetPixel(index3D); if (m_Parameters.m_SignalGen.m_NumberOfCoils>1) { dwiPix[g] += magn*magn; phasePix[g] += phase*phase; } else { dwiPix[g] = magn; phasePix[g] = phase; } //#pragma omp critical { magnitudeDwiImage->SetPixel(index3D, dwiPix); m_PhaseImage->SetPixel(index3D, phasePix); // k-space image if (g==0) { DoubleDwiType::PixelType kspacePix = m_KspaceImage->GetPixel(index3D); kspacePix[c] = idft->GetKSpaceImage()->GetPixel(index2D); m_KspaceImage->SetPixel(index3D, kspacePix); } } } } if (m_Parameters.m_SignalGen.m_NumberOfCoils>1) { for (int y=0; y(magnitudeDwiImage->GetLargestPossibleRegion().GetSize(1)); y++) for (int x=0; x(magnitudeDwiImage->GetLargestPossibleRegion().GetSize(0)); x++) { DoubleDwiType::IndexType index3D; index3D[0]=x; index3D[1]=y; index3D[2]=z; DoubleDwiType::PixelType magPix = magnitudeDwiImage->GetPixel(index3D); magPix[g] = sqrt(magPix[g]/m_Parameters.m_SignalGen.m_NumberOfCoils); DoubleDwiType::PixelType phasePix = m_PhaseImage->GetPixel(index3D); phasePix[g] = sqrt(phasePix[g]/m_Parameters.m_SignalGen.m_NumberOfCoils); //#pragma omp critical { magnitudeDwiImage->SetPixel(index3D, magPix); m_PhaseImage->SetPixel(index3D, phasePix); } } } ++disp; unsigned long newTick = 50*disp.count()/disp.expected_count(); for (unsigned long tick = 0; tick<(newTick-lastTick); tick++) PrintToLog("*", false, false, false); lastTick = newTick; } } PrintToLog("\n", false); return magnitudeDwiImage; } template< class PixelType > TractsToDWIImageFilter< PixelType >::ItkDoubleImgType::Pointer TractsToDWIImageFilter< PixelType >:: NormalizeInsideMask(ItkDoubleImgType::Pointer image) { double max = itk::NumericTraits< double >::min(); double min = itk::NumericTraits< double >::max(); itk::ImageRegionIterator< ItkDoubleImgType > it(image, image->GetLargestPossibleRegion()); while(!it.IsAtEnd()) { if (m_Parameters.m_SignalGen.m_MaskImage.IsNotNull() && m_Parameters.m_SignalGen.m_MaskImage->GetPixel(it.GetIndex())<=0) { it.Set(0.0); ++it; continue; } if (it.Get()>max) max = it.Get(); if (it.Get()::New(); scaler->SetInput(image); scaler->SetShift(-min); scaler->SetScale(1.0/(max-min)); scaler->Update(); return scaler->GetOutput(); } template< class PixelType > void TractsToDWIImageFilter< PixelType >::CheckVolumeFractionImages() { m_UseRelativeNonFiberVolumeFractions = false; // check for fiber volume fraction maps unsigned int fibVolImages = 0; for (std::size_t i=0; iGetVolumeFractionImage().IsNotNull()) { PrintToLog("Using volume fraction map for fiber compartment " + boost::lexical_cast(i+1), false); fibVolImages++; } } // check for non-fiber volume fraction maps unsigned int nonfibVolImages = 0; for (std::size_t i=0; iGetVolumeFractionImage().IsNotNull()) { PrintToLog("Using volume fraction map for non-fiber compartment " + boost::lexical_cast(i+1), false); nonfibVolImages++; } } // not all fiber compartments are using volume fraction maps // --> non-fiber volume fractions are assumed to be relative to the // non-fiber volume and not absolute voxel-volume fractions. // this means if two non-fiber compartments are used but only one of them // has an associated volume fraction map, the repesctive other volume fraction map // can be determined as inverse (1-val) of the present volume fraction map- if ( fibVolImages::New(); inverter->SetMaximum(1.0); if ( m_Parameters.m_NonFiberModelList[0]->GetVolumeFractionImage().IsNull() && m_Parameters.m_NonFiberModelList[1]->GetVolumeFractionImage().IsNotNull() ) { // m_Parameters.m_NonFiberModelList[1]->SetVolumeFractionImage( // NormalizeInsideMask( m_Parameters.m_NonFiberModelList[1]->GetVolumeFractionImage() ) ); inverter->SetInput( m_Parameters.m_NonFiberModelList[1]->GetVolumeFractionImage() ); inverter->Update(); m_Parameters.m_NonFiberModelList[0]->SetVolumeFractionImage(inverter->GetOutput()); } else if ( m_Parameters.m_NonFiberModelList[1]->GetVolumeFractionImage().IsNull() && m_Parameters.m_NonFiberModelList[0]->GetVolumeFractionImage().IsNotNull() ) { // m_Parameters.m_NonFiberModelList[0]->SetVolumeFractionImage( // NormalizeInsideMask( m_Parameters.m_NonFiberModelList[0]->GetVolumeFractionImage() ) ); inverter->SetInput( m_Parameters.m_NonFiberModelList[0]->GetVolumeFractionImage() ); inverter->Update(); m_Parameters.m_NonFiberModelList[1]->SetVolumeFractionImage(inverter->GetOutput()); } else { itkExceptionMacro("Something went wrong in automatically calculating the missing non-fiber volume fraction image!" " Did you use two non fiber compartments but only one volume fraction image?" " Then it should work and this error is really strange."); } m_UseRelativeNonFiberVolumeFractions = true; nonfibVolImages++; } // Up to two fiber compartments are allowed without volume fraction maps since the volume fractions can then be determined automatically if (m_Parameters.m_FiberModelList.size()>2 && fibVolImages!=m_Parameters.m_FiberModelList.size()) itkExceptionMacro("More than two fiber compartment selected but no corresponding volume fraction maps set!"); // One non-fiber compartment is allowed without volume fraction map since the volume fraction can then be determined automatically if (m_Parameters.m_NonFiberModelList.size()>1 && nonfibVolImages!=m_Parameters.m_NonFiberModelList.size()) itkExceptionMacro("More than one non-fiber compartment selected but no volume fraction maps set!"); if (fibVolImages0) { PrintToLog("Not all fiber compartments are using an associated volume fraction image.\n" "Assuming non-fiber volume fraction images to contain values relative to the" " remaining non-fiber volume, not absolute values.", false); m_UseRelativeNonFiberVolumeFractions = true; // mitk::LocaleSwitch localeSwitch("C"); // itk::ImageFileWriter::Pointer wr = itk::ImageFileWriter::New(); // wr->SetInput(m_Parameters.m_NonFiberModelList[1]->GetVolumeFractionImage()); // wr->SetFileName("/local/volumefraction.nrrd"); // wr->Update(); } // initialize the images that store the output volume fraction of each compartment m_VolumeFractions.clear(); for (std::size_t i=0; iSetSpacing( m_WorkingSpacing ); doubleImg->SetOrigin( m_WorkingOrigin ); doubleImg->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); doubleImg->SetLargestPossibleRegion( m_WorkingImageRegion ); doubleImg->SetBufferedRegion( m_WorkingImageRegion ); doubleImg->SetRequestedRegion( m_WorkingImageRegion ); doubleImg->Allocate(); doubleImg->FillBuffer(0); m_VolumeFractions.push_back(doubleImg); } } template< class PixelType > void TractsToDWIImageFilter< PixelType >::InitializeData() { m_Rotations.clear(); m_Translations.clear(); m_MotionLog = ""; m_SpikeLog = ""; + m_TickImage = nullptr; + m_RfImage = nullptr; + // initialize output dwi image m_Parameters.m_SignalGen.m_CroppedRegion = m_Parameters.m_SignalGen.m_ImageRegion; if (m_Parameters.m_Misc.m_DoAddAliasing) m_Parameters.m_SignalGen.m_CroppedRegion.SetSize( 1, m_Parameters.m_SignalGen.m_CroppedRegion.GetSize(1) *m_Parameters.m_SignalGen.m_CroppingFactor); itk::Point shiftedOrigin = m_Parameters.m_SignalGen.m_ImageOrigin; shiftedOrigin[1] += (m_Parameters.m_SignalGen.m_ImageRegion.GetSize(1) -m_Parameters.m_SignalGen.m_CroppedRegion.GetSize(1))*m_Parameters.m_SignalGen.m_ImageSpacing[1]/2; m_OutputImage = OutputImageType::New(); m_OutputImage->SetSpacing( m_Parameters.m_SignalGen.m_ImageSpacing ); m_OutputImage->SetOrigin( shiftedOrigin ); m_OutputImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); m_OutputImage->SetLargestPossibleRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_OutputImage->SetBufferedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_OutputImage->SetRequestedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); m_OutputImage->SetVectorLength( m_Parameters.m_SignalGen.GetNumVolumes() ); m_OutputImage->Allocate(); typename OutputImageType::PixelType temp; temp.SetSize(m_Parameters.m_SignalGen.GetNumVolumes()); temp.Fill(0.0); m_OutputImage->FillBuffer(temp); PrintToLog("Output image spacing: [" + boost::lexical_cast(m_Parameters.m_SignalGen.m_ImageSpacing[0]) + "," + boost::lexical_cast(m_Parameters.m_SignalGen.m_ImageSpacing[1]) + "," + boost::lexical_cast(m_Parameters.m_SignalGen.m_ImageSpacing[2]) + "]", false); PrintToLog("Output image size: [" + boost::lexical_cast(m_Parameters.m_SignalGen.m_CroppedRegion.GetSize(0)) + "," + boost::lexical_cast(m_Parameters.m_SignalGen.m_CroppedRegion.GetSize(1)) + "," + boost::lexical_cast(m_Parameters.m_SignalGen.m_CroppedRegion.GetSize(2)) + "]", false); // images containing real and imaginary part of the dMRI signal for each coil m_OutputImagesReal.clear(); m_OutputImagesImag.clear(); for (unsigned int i=0; iSetSpacing( m_Parameters.m_SignalGen.m_ImageSpacing ); outputImageReal->SetOrigin( shiftedOrigin ); outputImageReal->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); outputImageReal->SetLargestPossibleRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); outputImageReal->SetBufferedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); outputImageReal->SetRequestedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); outputImageReal->SetVectorLength( m_Parameters.m_SignalGen.GetNumVolumes() ); outputImageReal->Allocate(); outputImageReal->FillBuffer(temp); m_OutputImagesReal.push_back(outputImageReal); typename DoubleDwiType::Pointer outputImageImag = DoubleDwiType::New(); outputImageImag->SetSpacing( m_Parameters.m_SignalGen.m_ImageSpacing ); outputImageImag->SetOrigin( shiftedOrigin ); outputImageImag->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); outputImageImag->SetLargestPossibleRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); outputImageImag->SetBufferedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); outputImageImag->SetRequestedRegion( m_Parameters.m_SignalGen.m_CroppedRegion ); outputImageImag->SetVectorLength( m_Parameters.m_SignalGen.GetNumVolumes() ); outputImageImag->Allocate(); outputImageImag->FillBuffer(temp); m_OutputImagesImag.push_back(outputImageImag); } // Apply in-plane upsampling for Gibbs ringing artifact double upsampling = 1; if (m_Parameters.m_SignalGen.m_DoAddGibbsRinging && m_Parameters.m_SignalGen.m_ZeroRinging==0) upsampling = 2; m_WorkingSpacing = m_Parameters.m_SignalGen.m_ImageSpacing; m_WorkingSpacing[0] /= upsampling; m_WorkingSpacing[1] /= upsampling; m_WorkingImageRegion = m_Parameters.m_SignalGen.m_ImageRegion; m_WorkingImageRegion.SetSize(0, m_Parameters.m_SignalGen.m_ImageRegion.GetSize()[0]*upsampling); m_WorkingImageRegion.SetSize(1, m_Parameters.m_SignalGen.m_ImageRegion.GetSize()[1]*upsampling); m_WorkingOrigin = m_Parameters.m_SignalGen.m_ImageOrigin; m_WorkingOrigin[0] -= m_Parameters.m_SignalGen.m_ImageSpacing[0]/2; m_WorkingOrigin[0] += m_WorkingSpacing[0]/2; m_WorkingOrigin[1] -= m_Parameters.m_SignalGen.m_ImageSpacing[1]/2; m_WorkingOrigin[1] += m_WorkingSpacing[1]/2; m_WorkingOrigin[2] -= m_Parameters.m_SignalGen.m_ImageSpacing[2]/2; m_WorkingOrigin[2] += m_WorkingSpacing[2]/2; m_VoxelVolume = m_WorkingSpacing[0]*m_WorkingSpacing[1]*m_WorkingSpacing[2]; PrintToLog("Working image spacing: [" + boost::lexical_cast(m_WorkingSpacing[0]) + "," + boost::lexical_cast(m_WorkingSpacing[1]) + "," + boost::lexical_cast(m_WorkingSpacing[2]) + "]", false); PrintToLog("Working image size: [" + boost::lexical_cast(m_WorkingImageRegion.GetSize(0)) + "," + boost::lexical_cast(m_WorkingImageRegion.GetSize(1)) + "," + boost::lexical_cast(m_WorkingImageRegion.GetSize(2)) + "]", false); // generate double images to store the individual compartment signals m_CompartmentImages.clear(); int numFiberCompartments = m_Parameters.m_FiberModelList.size(); int numNonFiberCompartments = m_Parameters.m_NonFiberModelList.size(); for (int i=0; iSetSpacing( m_WorkingSpacing ); doubleDwi->SetOrigin( m_WorkingOrigin ); doubleDwi->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); doubleDwi->SetLargestPossibleRegion( m_WorkingImageRegion ); doubleDwi->SetBufferedRegion( m_WorkingImageRegion ); doubleDwi->SetRequestedRegion( m_WorkingImageRegion ); doubleDwi->SetVectorLength( m_Parameters.m_SignalGen.GetNumVolumes() ); doubleDwi->Allocate(); DoubleDwiType::PixelType pix; pix.SetSize(m_Parameters.m_SignalGen.GetNumVolumes()); pix.Fill(0.0); doubleDwi->FillBuffer(pix); m_CompartmentImages.push_back(doubleDwi); } if (m_FiberBundle.IsNull() && m_InputImage.IsNotNull()) { m_CompartmentImages.clear(); m_Parameters.m_SignalGen.m_DoAddMotion = false; m_Parameters.m_SignalGen.m_DoSimulateRelaxation = false; PrintToLog("Simulating acquisition for input diffusion-weighted image.", false); auto caster = itk::CastImageFilter< OutputImageType, DoubleDwiType >::New(); caster->SetInput(m_InputImage); caster->Update(); if (m_Parameters.m_SignalGen.m_DoAddGibbsRinging && m_Parameters.m_SignalGen.m_ZeroRinging==0) { PrintToLog("Upsampling input diffusion-weighted image for Gibbs ringing simulation.", false); auto resampler = itk::ResampleDwiImageFilter< double >::New(); resampler->SetInput(caster->GetOutput()); itk::Vector< double, 3 > samplingFactor; samplingFactor[0] = upsampling; samplingFactor[1] = upsampling; samplingFactor[2] = 1; resampler->SetSamplingFactor(samplingFactor); resampler->SetInterpolation(itk::ResampleDwiImageFilter< double >::Interpolate_WindowedSinc); resampler->Update(); m_CompartmentImages.push_back(resampler->GetOutput()); } else m_CompartmentImages.push_back(caster->GetOutput()); VectorType translation; translation.Fill(0.0); MatrixType rotation; rotation.SetIdentity(); for (unsigned int g=0; gGetLargestPossibleRegion()!=m_WorkingImageRegion) { PrintToLog("Resampling tissue mask", false); // rescale mask image (otherwise there are problems with the resampling) auto rescaler = itk::RescaleIntensityImageFilter::New(); rescaler->SetInput(0,m_Parameters.m_SignalGen.m_MaskImage); rescaler->SetOutputMaximum(100); rescaler->SetOutputMinimum(0); rescaler->Update(); // resample mask image auto resampler = itk::ResampleImageFilter::New(); resampler->SetInput(rescaler->GetOutput()); resampler->SetSize(m_WorkingImageRegion.GetSize()); resampler->SetOutputSpacing(m_WorkingSpacing); resampler->SetOutputOrigin(m_WorkingOrigin); resampler->SetOutputDirection(m_Parameters.m_SignalGen.m_ImageDirection); resampler->SetOutputStartIndex ( m_WorkingImageRegion.GetIndex() ); auto nn_interpolator = itk::NearestNeighborInterpolateImageFunction::New(); resampler->SetInterpolator(nn_interpolator); resampler->Update(); m_Parameters.m_SignalGen.m_MaskImage = resampler->GetOutput(); } // resample frequency map if (m_Parameters.m_SignalGen.m_FrequencyMap.IsNotNull() && m_Parameters.m_SignalGen.m_FrequencyMap->GetLargestPossibleRegion()!=m_WorkingImageRegion) { PrintToLog("Resampling frequency map", false); auto resampler = itk::ResampleImageFilter::New(); resampler->SetInput(m_Parameters.m_SignalGen.m_FrequencyMap); resampler->SetSize(m_WorkingImageRegion.GetSize()); resampler->SetOutputSpacing(m_WorkingSpacing); resampler->SetOutputOrigin(m_WorkingOrigin); resampler->SetOutputDirection(m_Parameters.m_SignalGen.m_ImageDirection); resampler->SetOutputStartIndex ( m_WorkingImageRegion.GetIndex() ); auto nn_interpolator = itk::NearestNeighborInterpolateImageFunction::New(); resampler->SetInterpolator(nn_interpolator); resampler->Update(); m_Parameters.m_SignalGen.m_FrequencyMap = resampler->GetOutput(); } m_MaskImageSet = true; if (m_Parameters.m_SignalGen.m_MaskImage.IsNull()) { // no input tissue mask is set -> create default PrintToLog("No tissue mask set", false); m_Parameters.m_SignalGen.m_MaskImage = ItkUcharImgType::New(); m_Parameters.m_SignalGen.m_MaskImage->SetSpacing( m_WorkingSpacing ); m_Parameters.m_SignalGen.m_MaskImage->SetOrigin( m_WorkingOrigin ); m_Parameters.m_SignalGen.m_MaskImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); m_Parameters.m_SignalGen.m_MaskImage->SetLargestPossibleRegion( m_WorkingImageRegion ); m_Parameters.m_SignalGen.m_MaskImage->SetBufferedRegion( m_WorkingImageRegion ); m_Parameters.m_SignalGen.m_MaskImage->SetRequestedRegion( m_WorkingImageRegion ); m_Parameters.m_SignalGen.m_MaskImage->Allocate(); m_Parameters.m_SignalGen.m_MaskImage->FillBuffer(100); m_MaskImageSet = false; } else { PrintToLog("Using tissue mask", false); } if (m_Parameters.m_SignalGen.m_DoAddMotion) { if (m_Parameters.m_SignalGen.m_DoRandomizeMotion) { PrintToLog("Random motion artifacts:", false); PrintToLog("Maximum rotation: +/-" + boost::lexical_cast(m_Parameters.m_SignalGen.m_Rotation) + "°", false); PrintToLog("Maximum translation: +/-" + boost::lexical_cast(m_Parameters.m_SignalGen.m_Translation) + "mm", false); } else { PrintToLog("Linear motion artifacts:", false); PrintToLog("Maximum rotation: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_Rotation) + "°", false); PrintToLog("Maximum translation: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_Translation) + "mm", false); } } if ( m_Parameters.m_SignalGen.m_MotionVolumes.empty() ) { // no motion in first volume m_Parameters.m_SignalGen.m_MotionVolumes.push_back(false); // motion in all other volumes while ( m_Parameters.m_SignalGen.m_MotionVolumes.size() < m_Parameters.m_SignalGen.GetNumVolumes() ) { m_Parameters.m_SignalGen.m_MotionVolumes.push_back(true); } } // we need to know for every volume if there is motion. if this information is missing, then set corresponding fal to false while ( m_Parameters.m_SignalGen.m_MotionVolumes.size()::New(); duplicator->SetInputImage(m_Parameters.m_SignalGen.m_MaskImage); duplicator->Update(); m_TransformedMaskImage = duplicator->GetOutput(); // second upsampling needed for motion artifacts ImageRegion<3> upsampledImageRegion = m_WorkingImageRegion; VectorType upsampledSpacing = m_WorkingSpacing; upsampledSpacing[0] /= 4; upsampledSpacing[1] /= 4; upsampledSpacing[2] /= 4; upsampledImageRegion.SetSize(0, m_WorkingImageRegion.GetSize()[0]*4); upsampledImageRegion.SetSize(1, m_WorkingImageRegion.GetSize()[1]*4); upsampledImageRegion.SetSize(2, m_WorkingImageRegion.GetSize()[2]*4); itk::Point upsampledOrigin = m_WorkingOrigin; upsampledOrigin[0] -= m_WorkingSpacing[0]/2; upsampledOrigin[0] += upsampledSpacing[0]/2; upsampledOrigin[1] -= m_WorkingSpacing[1]/2; upsampledOrigin[1] += upsampledSpacing[1]/2; upsampledOrigin[2] -= m_WorkingSpacing[2]/2; upsampledOrigin[2] += upsampledSpacing[2]/2; m_UpsampledMaskImage = ItkUcharImgType::New(); auto upsampler = itk::ResampleImageFilter::New(); upsampler->SetInput(m_Parameters.m_SignalGen.m_MaskImage); upsampler->SetOutputParametersFromImage(m_Parameters.m_SignalGen.m_MaskImage); upsampler->SetSize(upsampledImageRegion.GetSize()); upsampler->SetOutputSpacing(upsampledSpacing); upsampler->SetOutputOrigin(upsampledOrigin); auto nn_interpolator = itk::NearestNeighborInterpolateImageFunction::New(); upsampler->SetInterpolator(nn_interpolator); upsampler->Update(); m_UpsampledMaskImage = upsampler->GetOutput(); } template< class PixelType > void TractsToDWIImageFilter< PixelType >::InitializeFiberData() { m_mmRadius = m_Parameters.m_SignalGen.m_AxonRadius/1000; auto caster = itk::CastImageFilter< itk::Image, itk::Image >::New(); caster->SetInput(m_TransformedMaskImage); caster->Update(); vtkSmartPointer weights = m_FiberBundle->GetFiberWeights(); float mean_weight = 0; for (int i=0; iGetSize(); i++) mean_weight += weights->GetValue(i); mean_weight /= weights->GetSize(); if (mean_weight>0.000001) for (int i=0; iGetSize(); i++) m_FiberBundle->SetFiberWeight(i, weights->GetValue(i)/mean_weight); else PrintToLog("\nWarning: streamlines have VERY low weights. Average weight: " + boost::lexical_cast(mean_weight) + ". Possible source of calculation errors.", false, true, true); auto density_calculator = itk::TractDensityImageFilter< itk::Image >::New(); density_calculator->SetFiberBundle(m_FiberBundle); density_calculator->SetInputImage(caster->GetOutput()); density_calculator->SetBinaryOutput(false); density_calculator->SetUseImageGeometry(true); density_calculator->SetOutputAbsoluteValues(true); density_calculator->Update(); double max_density = density_calculator->GetMaxDensity(); double voxel_volume = m_WorkingSpacing[0]*m_WorkingSpacing[1]*m_WorkingSpacing[2]; if (m_mmRadius>0) { std::stringstream stream; stream << std::fixed << setprecision(2) << itk::Math::pi*m_mmRadius*m_mmRadius*max_density; std::string s = stream.str(); PrintToLog("\nMax. fiber volume: " + s + "mm².", false, true, true); { double full_radius = 1000*std::sqrt(voxel_volume/(max_density*itk::Math::pi)); std::stringstream stream; stream << std::fixed << setprecision(2) << full_radius; std::string s = stream.str(); PrintToLog("\nA full fiber voxel corresponds to a fiber radius of ~" + s + "µm, given the current fiber configuration.", false, true, true); } } else { m_mmRadius = std::sqrt(voxel_volume/(max_density*itk::Math::pi)); std::stringstream stream; stream << std::fixed << setprecision(2) << m_mmRadius*1000; std::string s = stream.str(); PrintToLog("\nSetting fiber radius to " + s + "µm to obtain full voxel.", false, true, true); } // a second fiber bundle is needed to store the transformed version of the m_FiberBundleWorkingCopy m_FiberBundleTransformed = m_FiberBundle->GetDeepCopy(); } template< class PixelType > bool TractsToDWIImageFilter< PixelType >::PrepareLogFile() { if(m_Logfile.is_open()) m_Logfile.close(); std::string filePath; std::string fileName; // Get directory name: if (m_Parameters.m_Misc.m_OutputPath.size() > 0) { filePath = m_Parameters.m_Misc.m_OutputPath; if( *(--(filePath.cend())) != '/') { filePath.push_back('/'); } } else return false; // Get file name: if( ! m_Parameters.m_Misc.m_ResultNode->GetName().empty() ) { fileName = m_Parameters.m_Misc.m_ResultNode->GetName(); } else { fileName = ""; } if( ! m_Parameters.m_Misc.m_OutputPrefix.empty() ) { fileName = m_Parameters.m_Misc.m_OutputPrefix + fileName; } try { m_Logfile.open( ( filePath + '/' + fileName + ".log" ).c_str() ); } catch (const std::ios_base::failure &fail) { MITK_ERROR << "itkTractsToDWIImageFilter.cpp: Exception " << fail.what() << " while trying to open file" << filePath << '/' << fileName << ".log"; return false; } if ( m_Logfile.is_open() ) { PrintToLog( "Logfile: " + filePath + '/' + fileName + ".log", false ); return true; } else return false; } template< class PixelType > void TractsToDWIImageFilter< PixelType >::GenerateData() { PrintToLog("\n**********************************************", false); // prepare logfile PrepareLogFile(); PrintToLog("Starting Fiberfox dMRI simulation"); m_TimeProbe.Start(); // check input data if (m_FiberBundle.IsNull() && m_InputImage.IsNull()) itkExceptionMacro("Input fiber bundle and input diffusion-weighted image is nullptr!"); if (m_Parameters.m_FiberModelList.empty() && m_InputImage.IsNull()) itkExceptionMacro("No diffusion model for fiber compartments defined and input diffusion-weighted" " image is nullptr! At least one fiber compartment is necessary to simulate diffusion."); if (m_Parameters.m_NonFiberModelList.empty() && m_InputImage.IsNull()) itkExceptionMacro("No diffusion model for non-fiber compartments defined and input diffusion-weighted" " image is nullptr! At least one non-fiber compartment is necessary to simulate diffusion."); if (m_Parameters.m_SignalGen.m_DoDisablePartialVolume) // no partial volume? remove all but first fiber compartment while (m_Parameters.m_FiberModelList.size()>1) m_Parameters.m_FiberModelList.pop_back(); if (!m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition) // No upsampling of input image needed if no k-space simulation is performed m_Parameters.m_SignalGen.m_DoAddGibbsRinging = false; if (m_UseConstantRandSeed) // always generate the same random numbers? m_RandGen->SetSeed(0); else m_RandGen->SetSeed(); InitializeData(); if ( m_FiberBundle.IsNotNull() ) // if no fiber bundle is found, we directly proceed to the k-space acquisition simulation { CheckVolumeFractionImages(); InitializeFiberData(); int numFiberCompartments = m_Parameters.m_FiberModelList.size(); int numNonFiberCompartments = m_Parameters.m_NonFiberModelList.size(); double maxVolume = 0; unsigned long lastTick = 0; int signalModelSeed = m_RandGen->GetIntegerVariate(); PrintToLog("\n", false, false); PrintToLog("Generating " + boost::lexical_cast(numFiberCompartments+numNonFiberCompartments) + "-compartment diffusion-weighted signal."); std::vector< int > bVals = m_Parameters.m_SignalGen.GetBvalues(); PrintToLog("b-values: ", false, false, true); for (auto v : bVals) PrintToLog(boost::lexical_cast(v) + " ", false, false, true); PrintToLog("\nVolumes: " + boost::lexical_cast(m_Parameters.m_SignalGen.GetNumVolumes()), false, true, true); PrintToLog("\n", false, false, true); PrintToLog("\n", false, false, true); unsigned int image_size_x = m_WorkingImageRegion.GetSize(0); unsigned int region_size_y = m_WorkingImageRegion.GetSize(1); unsigned int num_gradients = m_Parameters.m_SignalGen.GetNumVolumes(); int numFibers = m_FiberBundle->GetNumFibers(); boost::progress_display disp(numFibers*num_gradients); if (m_FiberBundle->GetMeanFiberLength()<5.0) omp_set_num_threads(2); PrintToLog("0% 10 20 30 40 50 60 70 80 90 100%", false, true, false); PrintToLog("|----|----|----|----|----|----|----|----|----|----|\n*", false, false, false); for (unsigned int g=0; gSetSeed(signalModelSeed); for (std::size_t i=0; iSetSeed(signalModelSeed); // storing voxel-wise intra-axonal volume in mm³ auto intraAxonalVolumeImage = ItkDoubleImgType::New(); intraAxonalVolumeImage->SetSpacing( m_WorkingSpacing ); intraAxonalVolumeImage->SetOrigin( m_WorkingOrigin ); intraAxonalVolumeImage->SetDirection( m_Parameters.m_SignalGen.m_ImageDirection ); intraAxonalVolumeImage->SetLargestPossibleRegion( m_WorkingImageRegion ); intraAxonalVolumeImage->SetBufferedRegion( m_WorkingImageRegion ); intraAxonalVolumeImage->SetRequestedRegion( m_WorkingImageRegion ); intraAxonalVolumeImage->Allocate(); intraAxonalVolumeImage->FillBuffer(0); maxVolume = 0; double* intraAxBuffer = intraAxonalVolumeImage->GetBufferPointer(); if (this->GetAbortGenerateData()) continue; vtkPolyData* fiberPolyData = m_FiberBundleTransformed->GetFiberPolyData(); // generate fiber signal (if there are any fiber models present) if (!m_Parameters.m_FiberModelList.empty()) { std::vector< double* > buffers; for (unsigned int i=0; iGetBufferPointer()); #pragma omp parallel for for( int i=0; iGetAbortGenerateData()) continue; float fiberWeight = m_FiberBundleTransformed->GetFiberWeight(i); int numPoints = -1; std::vector< itk::Vector > points_copy; #pragma omp critical { vtkCell* cell = fiberPolyData->GetCell(i); numPoints = cell->GetNumberOfPoints(); vtkPoints* points = cell->GetPoints(); for (int j=0; jGetPoint(j))); } if (numPoints<2) continue; double seg_volume = fiberWeight*itk::Math::pi*m_mmRadius*m_mmRadius; for( int j=0; jGetAbortGenerateData()) { j=numPoints; continue; } itk::Vector v = points_copy.at(j); itk::Vector dir = points_copy.at(j+1)-v; if ( dir.GetSquaredNorm()<0.0001 || dir[0]!=dir[0] || dir[1]!=dir[1] || dir[2]!=dir[2] ) continue; dir.Normalize(); itk::Point startVertex = points_copy.at(j); itk::Index<3> startIndex; itk::ContinuousIndex startIndexCont; m_TransformedMaskImage->TransformPhysicalPointToIndex(startVertex, startIndex); m_TransformedMaskImage->TransformPhysicalPointToContinuousIndex(startVertex, startIndexCont); itk::Point endVertex = points_copy.at(j+1); itk::Index<3> endIndex; itk::ContinuousIndex endIndexCont; m_TransformedMaskImage->TransformPhysicalPointToIndex(endVertex, endIndex); m_TransformedMaskImage->TransformPhysicalPointToContinuousIndex(endVertex, endIndexCont); std::vector< std::pair< itk::Index<3>, double > > segments = mitk::imv::IntersectImage(m_WorkingSpacing, startIndex, endIndex, startIndexCont, endIndexCont); // generate signal for each fiber compartment for (int k=0; kSimulateMeasurement(g, dir)*seg_volume; for (std::pair< itk::Index<3>, double > seg : segments) { if (!m_TransformedMaskImage->GetLargestPossibleRegion().IsInside(seg.first) || m_TransformedMaskImage->GetPixel(seg.first)<=0) continue; double seg_signal = seg.second*signal_add; unsigned int linear_index = g + num_gradients*seg.first[0] + num_gradients*image_size_x*seg.first[1] + num_gradients*image_size_x*region_size_y*seg.first[2]; // update dMRI volume #pragma omp atomic buffers[k][linear_index] += seg_signal; // update fiber volume image if (k==0) { linear_index = seg.first[0] + image_size_x*seg.first[1] + image_size_x*region_size_y*seg.first[2]; #pragma omp atomic intraAxBuffer[linear_index] += seg.second*seg_volume; double vol = intraAxBuffer[linear_index]; if (vol>maxVolume) { maxVolume = vol; } } } } } #pragma omp critical { // progress report ++disp; unsigned long newTick = 50*disp.count()/disp.expected_count(); for (unsigned int tick = 0; tick<(newTick-lastTick); ++tick) PrintToLog("*", false, false, false); lastTick = newTick; } } } // axon radius not manually defined --> set fullest voxel (maxVolume) to full fiber voxel double density_correctiony_global = 1.0; if (m_Parameters.m_SignalGen.m_AxonRadius<0.0001) density_correctiony_global = m_VoxelVolume/maxVolume; // generate non-fiber signal ImageRegionIterator it3(m_TransformedMaskImage, m_TransformedMaskImage->GetLargestPossibleRegion()); while(!it3.IsAtEnd()) { if (it3.Get()>0) { DoubleDwiType::IndexType index = it3.GetIndex(); double iAxVolume = intraAxonalVolumeImage->GetPixel(index); // get non-transformed point (remove headmotion tranformation) // this point lives in the volume fraction image space itk::Point volume_fraction_point; if ( m_Parameters.m_SignalGen.m_DoAddMotion ) volume_fraction_point = GetMovedPoint(index, false); else m_TransformedMaskImage->TransformIndexToPhysicalPoint(index, volume_fraction_point); if (m_Parameters.m_SignalGen.m_DoDisablePartialVolume) { if (iAxVolume>0.0001) // scale fiber compartment to voxel { DoubleDwiType::PixelType pix = m_CompartmentImages.at(0)->GetPixel(index); pix[g] *= m_VoxelVolume/iAxVolume; m_CompartmentImages.at(0)->SetPixel(index, pix); if (g==0) m_VolumeFractions.at(0)->SetPixel(index, 1); } else { DoubleDwiType::PixelType pix = m_CompartmentImages.at(0)->GetPixel(index); pix[g] = 0; m_CompartmentImages.at(0)->SetPixel(index, pix); SimulateExtraAxonalSignal(index, volume_fraction_point, 0, g); } } else { // manually defined axon radius and voxel overflow --> rescale to voxel volume if ( m_Parameters.m_SignalGen.m_AxonRadius>=0.0001 && iAxVolume>m_VoxelVolume ) { for (int i=0; iGetPixel(index); pix[g] *= m_VoxelVolume/iAxVolume; m_CompartmentImages.at(i)->SetPixel(index, pix); } iAxVolume = m_VoxelVolume; } // if volume fraction image is set use it, otherwise use global scaling factor double density_correction_voxel = density_correctiony_global; if ( m_Parameters.m_FiberModelList[0]->GetVolumeFractionImage()!=nullptr && iAxVolume>0.0001 ) { m_DoubleInterpolator->SetInputImage(m_Parameters.m_FiberModelList[0]->GetVolumeFractionImage()); double volume_fraction = mitk::imv::GetImageValue(volume_fraction_point, true, m_DoubleInterpolator); if (volume_fraction<0) mitkThrow() << "Volume fraction image (index 1) contains negative values (intra-axonal compartment)!"; density_correction_voxel = m_VoxelVolume*volume_fraction/iAxVolume; // remove iAxVolume sclaing and scale to volume_fraction } else if (m_Parameters.m_FiberModelList[0]->GetVolumeFractionImage()!=nullptr) density_correction_voxel = 0.0; // adjust intra-axonal compartment volume by density correction factor DoubleDwiType::PixelType pix = m_CompartmentImages.at(0)->GetPixel(index); pix[g] *= density_correction_voxel; m_CompartmentImages.at(0)->SetPixel(index, pix); // normalize remaining fiber volume fractions (they are rescaled in SimulateExtraAxonalSignal) if (iAxVolume>0.0001) { for (int i=1; iGetPixel(index); pix[g] /= iAxVolume; m_CompartmentImages.at(i)->SetPixel(index, pix); } } else { for (int i=1; iGetPixel(index); pix[g] = 0; m_CompartmentImages.at(i)->SetPixel(index, pix); } } iAxVolume = density_correction_voxel*iAxVolume; // new intra-axonal volume = old intra-axonal volume * correction factor // simulate other compartments SimulateExtraAxonalSignal(index, volume_fraction_point, iAxVolume, g); } } ++it3; } } PrintToLog("\n", false); } if (this->GetAbortGenerateData()) { PrintToLog("\n", false, false); PrintToLog("Simulation aborted"); return; } DoubleDwiType::Pointer doubleOutImage; double signalScale = m_Parameters.m_SignalGen.m_SignalScale; if ( m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition ) // do k-space stuff { PrintToLog("\n", false, false); PrintToLog("Simulating k-space acquisition using " +boost::lexical_cast(m_Parameters.m_SignalGen.m_NumberOfCoils) +" coil(s)"); switch (m_Parameters.m_SignalGen.m_AcquisitionType) { case SignalGenerationParameters::SingleShotEpi: { PrintToLog("Acquisition type: single shot EPI", false); break; } case SignalGenerationParameters::ConventionalSpinEcho: { PrintToLog("Acquisition type: conventional spin echo (one RF pulse per line) with cartesian k-space trajectory", false); break; } case SignalGenerationParameters::FastSpinEcho: { PrintToLog("Acquisition type: fast spin echo (one RF pulse per ETL lines) with cartesian k-space trajectory (ETL: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_EchoTrainLength) + ")", false); - PrintToLog("Effective TE: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_tEcho*m_Parameters.m_SignalGen.m_EchoTrainLength/2), false); break; } default: { PrintToLog("Acquisition type: single shot EPI", false); break; } } if(m_Parameters.m_SignalGen.m_tInv>0) PrintToLog("Using inversion pulse with TI " + boost::lexical_cast(m_Parameters.m_SignalGen.m_tInv) + "ms", false); if (m_Parameters.m_SignalGen.m_DoSimulateRelaxation) PrintToLog("Simulating signal relaxation", false); if (m_Parameters.m_SignalGen.m_NoiseVariance>0 && m_Parameters.m_Misc.m_DoAddNoise) PrintToLog("Simulating complex Gaussian noise: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_NoiseVariance), false); if (m_Parameters.m_SignalGen.m_FrequencyMap.IsNotNull() && m_Parameters.m_Misc.m_DoAddDistortions) PrintToLog("Simulating distortions", false); if (m_Parameters.m_SignalGen.m_DoAddGibbsRinging) { if (m_Parameters.m_SignalGen.m_ZeroRinging > 0) PrintToLog("Simulating ringing artifacts by zeroing " + boost::lexical_cast(m_Parameters.m_SignalGen.m_ZeroRinging) + "% of k-space frequencies", false); else PrintToLog("Simulating ringing artifacts by cropping high resolution inputs during k-space simulation", false); } if (m_Parameters.m_Misc.m_DoAddEddyCurrents && m_Parameters.m_SignalGen.m_EddyStrength>0) PrintToLog("Simulating eddy currents: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_EddyStrength), false); if (m_Parameters.m_Misc.m_DoAddSpikes && m_Parameters.m_SignalGen.m_Spikes>0) PrintToLog("Simulating spikes: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_Spikes), false); if (m_Parameters.m_Misc.m_DoAddAliasing && m_Parameters.m_SignalGen.m_CroppingFactor<1.0) PrintToLog("Simulating aliasing: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_CroppingFactor), false); if (m_Parameters.m_Misc.m_DoAddGhosts && m_Parameters.m_SignalGen.m_KspaceLineOffset>0) PrintToLog("Simulating ghosts: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_KspaceLineOffset), false); doubleOutImage = SimulateKspaceAcquisition(m_CompartmentImages); signalScale = 1; // already scaled in SimulateKspaceAcquisition() } else // don't do k-space stuff, just sum compartments { PrintToLog("Summing compartments"); doubleOutImage = m_CompartmentImages.at(0); for (unsigned int i=1; i::New(); adder->SetInput1(doubleOutImage); adder->SetInput2(m_CompartmentImages.at(i)); adder->Update(); doubleOutImage = adder->GetOutput(); } } if (this->GetAbortGenerateData()) { PrintToLog("\n", false, false); PrintToLog("Simulation aborted"); return; } PrintToLog("Finalizing image"); if (m_Parameters.m_SignalGen.m_DoAddDrift && m_Parameters.m_SignalGen.m_Drift>0.0) PrintToLog("Adding signal drift: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_Drift), false); if (signalScale>1) PrintToLog("Scaling signal", false); if (m_Parameters.m_NoiseModel) PrintToLog("Adding noise: " + boost::lexical_cast(m_Parameters.m_SignalGen.m_NoiseVariance), false); ImageRegionIterator it4 (m_OutputImage, m_OutputImage->GetLargestPossibleRegion()); DoubleDwiType::PixelType signal; signal.SetSize(m_Parameters.m_SignalGen.GetNumVolumes()); boost::progress_display disp2(m_OutputImage->GetLargestPossibleRegion().GetNumberOfPixels()); PrintToLog("0% 10 20 30 40 50 60 70 80 90 100%", false, true, false); PrintToLog("|----|----|----|----|----|----|----|----|----|----|\n*", false, false, false); int lastTick = 0; while(!it4.IsAtEnd()) { if (this->GetAbortGenerateData()) { PrintToLog("\n", false, false); PrintToLog("Simulation aborted"); return; } ++disp2; unsigned long newTick = 50*disp2.count()/disp2.expected_count(); for (unsigned long tick = 0; tick<(newTick-lastTick); tick++) PrintToLog("*", false, false, false); lastTick = newTick; typename OutputImageType::IndexType index = it4.GetIndex(); signal = doubleOutImage->GetPixel(index)*signalScale; for (unsigned int i=0; iAddNoise(signal); for (unsigned int i=0; i0) signal[i] = floor(signal[i]+0.5); else signal[i] = ceil(signal[i]-0.5); } it4.Set(signal); ++it4; } this->SetNthOutput(0, m_OutputImage); PrintToLog("\n", false); PrintToLog("Finished simulation"); m_TimeProbe.Stop(); if (m_Parameters.m_SignalGen.m_DoAddMotion) { PrintToLog("\nHead motion log:", false); PrintToLog(m_MotionLog, false, false); } if (m_Parameters.m_Misc.m_DoAddSpikes && m_Parameters.m_SignalGen.m_Spikes>0) { PrintToLog("\nSpike log:", false); PrintToLog(m_SpikeLog, false, false); } if (m_Logfile.is_open()) m_Logfile.close(); } template< class PixelType > void TractsToDWIImageFilter< PixelType >::PrintToLog(std::string m, bool addTime, bool linebreak, bool stdOut) { // timestamp if (addTime) { if ( m_Logfile.is_open() ) m_Logfile << this->GetTime() << " > "; m_StatusText += this->GetTime() + " > "; if (stdOut) std::cout << this->GetTime() << " > "; } // message if (m_Logfile.is_open()) m_Logfile << m; m_StatusText += m; if (stdOut) std::cout << m; // new line if (linebreak) { if (m_Logfile.is_open()) m_Logfile << "\n"; m_StatusText += "\n"; if (stdOut) std::cout << "\n"; } if ( m_Logfile.is_open() ) m_Logfile.flush(); } template< class PixelType > void TractsToDWIImageFilter< PixelType >::SimulateMotion(int g) { if ( m_Parameters.m_SignalGen.m_DoAddMotion && m_Parameters.m_SignalGen.m_DoRandomizeMotion && g>0 && m_Parameters.m_SignalGen.m_MotionVolumes[g-1]) { // The last volume was randomly moved, so we have to reset to fiberbundle and the mask. // Without motion or with linear motion, we keep the last position --> no reset. m_FiberBundleTransformed = m_FiberBundle->GetDeepCopy(); if (m_MaskImageSet) { auto duplicator = itk::ImageDuplicator::New(); duplicator->SetInputImage(m_Parameters.m_SignalGen.m_MaskImage); duplicator->Update(); m_TransformedMaskImage = duplicator->GetOutput(); } } VectorType rotation; VectorType translation; // is motion artifact enabled? // is the current volume g affected by motion? if ( m_Parameters.m_SignalGen.m_DoAddMotion && m_Parameters.m_SignalGen.m_MotionVolumes[g] && g(m_Parameters.m_SignalGen.GetNumVolumes()) ) { // adjust motion transforms if ( m_Parameters.m_SignalGen.m_DoRandomizeMotion ) { // randomly rotation[0] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Rotation[0]*2) -m_Parameters.m_SignalGen.m_Rotation[0]; rotation[1] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Rotation[1]*2) -m_Parameters.m_SignalGen.m_Rotation[1]; rotation[2] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Rotation[2]*2) -m_Parameters.m_SignalGen.m_Rotation[2]; translation[0] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Translation[0]*2) -m_Parameters.m_SignalGen.m_Translation[0]; translation[1] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Translation[1]*2) -m_Parameters.m_SignalGen.m_Translation[1]; translation[2] = m_RandGen->GetVariateWithClosedRange(m_Parameters.m_SignalGen.m_Translation[2]*2) -m_Parameters.m_SignalGen.m_Translation[2]; m_FiberBundleTransformed->TransformFibers(rotation[0], rotation[1], rotation[2], translation[0], translation[1], translation[2]); } else { // linearly rotation = m_Parameters.m_SignalGen.m_Rotation / m_NumMotionVolumes; translation = m_Parameters.m_SignalGen.m_Translation / m_NumMotionVolumes; m_MotionCounter++; m_FiberBundleTransformed->TransformFibers(rotation[0], rotation[1], rotation[2], translation[0], translation[1], translation[2]); rotation *= m_MotionCounter; translation *= m_MotionCounter; } MatrixType rotationMatrix = mitk::imv::GetRotationMatrixItk(rotation[0], rotation[1], rotation[2]); MatrixType rotationMatrixInv = mitk::imv::GetRotationMatrixItk(-rotation[0], -rotation[1], -rotation[2]); m_Rotations.push_back(rotationMatrix); m_RotationsInv.push_back(rotationMatrixInv); m_Translations.push_back(translation); // move mask image accoring to new transform if (m_MaskImageSet) { ImageRegionIterator maskIt(m_UpsampledMaskImage, m_UpsampledMaskImage->GetLargestPossibleRegion()); m_TransformedMaskImage->FillBuffer(0); while(!maskIt.IsAtEnd()) { if (maskIt.Get()<=0) { ++maskIt; continue; } DoubleDwiType::IndexType index = maskIt.GetIndex(); m_TransformedMaskImage->TransformPhysicalPointToIndex(GetMovedPoint(index, true), index); if (m_TransformedMaskImage->GetLargestPossibleRegion().IsInside(index)) m_TransformedMaskImage->SetPixel(index, 100); ++maskIt; } } } else { if (m_Parameters.m_SignalGen.m_DoAddMotion && !m_Parameters.m_SignalGen.m_DoRandomizeMotion && g>0) { rotation = m_Parameters.m_SignalGen.m_Rotation / m_NumMotionVolumes; rotation *= m_MotionCounter; m_Rotations.push_back(m_Rotations.back()); m_RotationsInv.push_back(m_RotationsInv.back()); m_Translations.push_back(m_Translations.back()); } else { rotation.Fill(0.0); VectorType translation; translation.Fill(0.0); MatrixType rotation_matrix; rotation_matrix.SetIdentity(); m_Rotations.push_back(rotation_matrix); m_RotationsInv.push_back(rotation_matrix); m_Translations.push_back(translation); } } if (m_Parameters.m_SignalGen.m_DoAddMotion) { m_MotionLog += boost::lexical_cast(g) + " rotation: " + boost::lexical_cast(rotation[0]) + "," + boost::lexical_cast(rotation[1]) + "," + boost::lexical_cast(rotation[2]) + ";"; m_MotionLog += " translation: " + boost::lexical_cast(m_Translations.back()[0]) + "," + boost::lexical_cast(m_Translations.back()[1]) + "," + boost::lexical_cast(m_Translations.back()[2]) + "\n"; } } template< class PixelType > itk::Point TractsToDWIImageFilter< PixelType >::GetMovedPoint(itk::Index<3>& index, bool forward) { itk::Point transformed_point; float tx = m_Translations.back()[0]; float ty = m_Translations.back()[1]; float tz = m_Translations.back()[2]; if (forward) { m_UpsampledMaskImage->TransformIndexToPhysicalPoint(index, transformed_point); m_FiberBundle->TransformPoint<>(transformed_point, m_Rotations.back(), tx, ty, tz); } else { tx *= -1; ty *= -1; tz *= -1; m_TransformedMaskImage->TransformIndexToPhysicalPoint(index, transformed_point); m_FiberBundle->TransformPoint<>(transformed_point, m_RotationsInv.back(), tx, ty, tz); } return transformed_point; } template< class PixelType > void TractsToDWIImageFilter< PixelType >:: SimulateExtraAxonalSignal(ItkUcharImgType::IndexType& index, itk::Point& volume_fraction_point, double intraAxonalVolume, int g) { int numFiberCompartments = m_Parameters.m_FiberModelList.size(); int numNonFiberCompartments = m_Parameters.m_NonFiberModelList.size(); if (m_Parameters.m_SignalGen.m_DoDisablePartialVolume) { // simulate signal for largest non-fiber compartment int max_compartment_index = 0; double max_fraction = 0; if (numNonFiberCompartments>1) { for (int i=0; iSetInputImage(m_Parameters.m_NonFiberModelList[i]->GetVolumeFractionImage()); double compartment_fraction = mitk::imv::GetImageValue(volume_fraction_point, true, m_DoubleInterpolator); if (compartment_fraction<0) mitkThrow() << "Volume fraction image (index " << i << ") contains values less than zero!"; if (compartment_fraction>max_fraction) { max_fraction = compartment_fraction; max_compartment_index = i; } } } DoubleDwiType::Pointer doubleDwi = m_CompartmentImages.at(max_compartment_index+numFiberCompartments); DoubleDwiType::PixelType pix = doubleDwi->GetPixel(index); pix[g] += m_Parameters.m_NonFiberModelList[max_compartment_index]->SimulateMeasurement(g, m_NullDir)*m_VoxelVolume; doubleDwi->SetPixel(index, pix); if (g==0) m_VolumeFractions.at(max_compartment_index+numFiberCompartments)->SetPixel(index, 1); } else { std::vector< double > fractions; if (g==0) m_VolumeFractions.at(0)->SetPixel(index, intraAxonalVolume/m_VoxelVolume); double extraAxonalVolume = m_VoxelVolume-intraAxonalVolume; // non-fiber volume if (extraAxonalVolume<0) { if (extraAxonalVolume<-0.001) MITK_ERROR << "Corrupted intra-axonal signal voxel detected. Fiber volume larger voxel volume! " << m_VoxelVolume << "<" << intraAxonalVolume; extraAxonalVolume = 0; } double interAxonalVolume = 0; if (numFiberCompartments>1) interAxonalVolume = extraAxonalVolume * intraAxonalVolume/m_VoxelVolume; // inter-axonal fraction of non fiber compartment double nonFiberVolume = extraAxonalVolume - interAxonalVolume; // rest of compartment if (nonFiberVolume<0) { if (nonFiberVolume<-0.001) MITK_ERROR << "Corrupted signal voxel detected. Fiber volume larger voxel volume!"; nonFiberVolume = 0; interAxonalVolume = extraAxonalVolume; } double compartmentSum = intraAxonalVolume; fractions.push_back(intraAxonalVolume/m_VoxelVolume); // rescale extra-axonal fiber signal for (int i=1; iGetVolumeFractionImage()!=nullptr) { m_DoubleInterpolator->SetInputImage(m_Parameters.m_FiberModelList[i]->GetVolumeFractionImage()); interAxonalVolume = mitk::imv::GetImageValue(volume_fraction_point, true, m_DoubleInterpolator)*m_VoxelVolume; if (interAxonalVolume<0) mitkThrow() << "Volume fraction image (index " << i+1 << ") contains negative values!"; } DoubleDwiType::PixelType pix = m_CompartmentImages.at(i)->GetPixel(index); pix[g] *= interAxonalVolume; m_CompartmentImages.at(i)->SetPixel(index, pix); compartmentSum += interAxonalVolume; fractions.push_back(interAxonalVolume/m_VoxelVolume); if (g==0) m_VolumeFractions.at(i)->SetPixel(index, interAxonalVolume/m_VoxelVolume); } for (int i=0; iGetVolumeFractionImage()!=nullptr) { m_DoubleInterpolator->SetInputImage(m_Parameters.m_NonFiberModelList[i]->GetVolumeFractionImage()); volume = mitk::imv::GetImageValue(volume_fraction_point, true, m_DoubleInterpolator)*m_VoxelVolume; if (volume<0) mitkThrow() << "Volume fraction image (index " << numFiberCompartments+i+1 << ") contains negative values (non-fiber compartment)!"; if (m_UseRelativeNonFiberVolumeFractions) volume *= nonFiberVolume/m_VoxelVolume; } DoubleDwiType::PixelType pix = m_CompartmentImages.at(i+numFiberCompartments)->GetPixel(index); pix[g] += m_Parameters.m_NonFiberModelList[i]->SimulateMeasurement(g, m_NullDir)*volume; m_CompartmentImages.at(i+numFiberCompartments)->SetPixel(index, pix); compartmentSum += volume; fractions.push_back(volume/m_VoxelVolume); if (g==0) m_VolumeFractions.at(i+numFiberCompartments)->SetPixel(index, volume/m_VoxelVolume); } if (compartmentSum/m_VoxelVolume>1.05) { MITK_ERROR << "Compartments do not sum to 1 in voxel " << index << " (" << compartmentSum/m_VoxelVolume << ")"; for (auto val : fractions) MITK_ERROR << val; } } } template< class PixelType > itk::Vector TractsToDWIImageFilter< PixelType >::GetItkVector(double point[3]) { itk::Vector itkVector; itkVector[0] = point[0]; itkVector[1] = point[1]; itkVector[2] = point[2]; return itkVector; } template< class PixelType > vnl_vector_fixed TractsToDWIImageFilter< PixelType >::GetVnlVector(double point[3]) { vnl_vector_fixed vnlVector; vnlVector[0] = point[0]; vnlVector[1] = point[1]; vnlVector[2] = point[2]; return vnlVector; } template< class PixelType > vnl_vector_fixed TractsToDWIImageFilter< PixelType >::GetVnlVector(Vector& vector) { vnl_vector_fixed vnlVector; vnlVector[0] = vector[0]; vnlVector[1] = vector[1]; vnlVector[2] = vector[2]; return vnlVector; } template< class PixelType > double TractsToDWIImageFilter< PixelType >::RoundToNearest(double num) { return (num > 0.0) ? floor(num + 0.5) : ceil(num - 0.5); } template< class PixelType > std::string TractsToDWIImageFilter< PixelType >::GetTime() { m_TimeProbe.Stop(); unsigned long total = RoundToNearest(m_TimeProbe.GetTotal()); unsigned long hours = total/3600; unsigned long minutes = (total%3600)/60; unsigned long seconds = total%60; std::string out = ""; out.append(boost::lexical_cast(hours)); out.append(":"); out.append(boost::lexical_cast(minutes)); out.append(":"); out.append(boost::lexical_cast(seconds)); m_TimeProbe.Start(); return out; } } diff --git a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.h b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.h index 5ed3333fbf..cefcee8683 100755 --- a/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.h +++ b/Modules/DiffusionImaging/FiberTracking/Fiberfox/itkTractsToDWIImageFilter.h @@ -1,172 +1,177 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef __itkTractsToDWIImageFilter_h__ #define __itkTractsToDWIImageFilter_h__ #include #include #include #include #include #include #include #include #include #include namespace itk { /** * \brief Generates artificial diffusion weighted image volume from the input fiberbundle using a generic multicompartment model. * See "Fiberfox: Facilitating the creation of realistic white matter software phantoms" (DOI: 10.1002/mrm.25045) for details. */ template< class PixelType > class TractsToDWIImageFilter : public ImageSource< itk::VectorImage< PixelType, 3 > > { public: typedef TractsToDWIImageFilter Self; typedef ImageSource< itk::VectorImage< PixelType, 3 > > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef typename Superclass::OutputImageType OutputImageType; typedef itk::Image ItkDoubleImgType4D; typedef itk::Image ItkDoubleImgType; typedef itk::Image ItkFloatImgType; typedef itk::Image ItkUcharImgType; typedef mitk::FiberBundle::Pointer FiberBundleType; typedef itk::VectorImage< double, 3 > DoubleDwiType; typedef itk::Matrix MatrixType; typedef itk::Image< float, 2 > Float2DImageType; typedef itk::Image< vcl_complex< float >, 2 > Complex2DImageType; typedef itk::Vector< float, 3> VectorType; itkFactorylessNewMacro(Self) itkCloneMacro(Self) itkTypeMacro( TractsToDWIImageFilter, ImageSource ) /** Input */ itkSetMacro( FiberBundle, FiberBundleType ) ///< Input fiber bundle itkSetMacro( InputImage, typename OutputImageType::Pointer ) ///< Input diffusion-weighted image. If no fiber bundle is set, then the acquisition is simulated for this image without a new diffusion simulation. itkSetMacro( UseConstantRandSeed, bool ) ///< Seed for random generator. void SetParameters( FiberfoxParameters param ) ///< Simulation parameters. { m_Parameters = param; } /** Output */ FiberfoxParameters GetParameters(){ return m_Parameters; } std::vector< ItkDoubleImgType::Pointer > GetVolumeFractions() ///< one double image for each compartment containing the corresponding volume fraction per voxel { return m_VolumeFractions; } itkGetMacro( StatusText, std::string ) itkGetMacro( PhaseImage, DoubleDwiType::Pointer ) itkGetMacro( KspaceImage, DoubleDwiType::Pointer ) itkGetMacro( CoilPointset, mitk::PointSet::Pointer ) + itkGetMacro( TickImage, typename Float2DImageType::Pointer ) ///< k-space readout ordering encoded in the voxels + itkGetMacro( RfImage, typename Float2DImageType::Pointer ) ///< time passed since last RF pulse encoded per voxel void GenerateData() override; std::vector GetOutputImagesReal() const { return m_OutputImagesReal; } std::vector GetOutputImagesImag() const { return m_OutputImagesImag; } protected: TractsToDWIImageFilter(); ~TractsToDWIImageFilter() override; itk::Vector GetItkVector(double point[3]); vnl_vector_fixed GetVnlVector(double point[3]); vnl_vector_fixed GetVnlVector(Vector< float, 3 >& vector); double RoundToNearest(double num); std::string GetTime(); bool PrepareLogFile(); /** Prepares the log file and returns true if successful or false if failed. */ void PrintToLog(std::string m, bool addTime=true, bool linebreak=true, bool stdOut=true); /** Transform generated image compartment by compartment, channel by channel and slice by slice using DFT and add k-space artifacts/effects. */ DoubleDwiType::Pointer SimulateKspaceAcquisition(std::vector< DoubleDwiType::Pointer >& images); /** Generate signal of non-fiber compartments. */ void SimulateExtraAxonalSignal(ItkUcharImgType::IndexType& index, itk::Point& volume_fraction_point, double intraAxonalVolume, int g); /** Move fibers to simulate headmotion */ void SimulateMotion(int g=-1); void CheckVolumeFractionImages(); ItkDoubleImgType::Pointer NormalizeInsideMask(ItkDoubleImgType::Pointer image); void InitializeData(); void InitializeFiberData(); itk::Point GetMovedPoint(itk::Index<3>& index, bool forward); // input mitk::FiberfoxParameters m_Parameters; FiberBundleType m_FiberBundle; typename OutputImageType::Pointer m_InputImage; // output typename OutputImageType::Pointer m_OutputImage; typename DoubleDwiType::Pointer m_PhaseImage; typename DoubleDwiType::Pointer m_KspaceImage; std::vector < typename DoubleDwiType::Pointer > m_OutputImagesReal; std::vector < typename DoubleDwiType::Pointer > m_OutputImagesImag; std::vector< ItkDoubleImgType::Pointer > m_VolumeFractions; std::string m_StatusText; // MISC itk::TimeProbe m_TimeProbe; bool m_UseConstantRandSeed; bool m_MaskImageSet; ofstream m_Logfile; std::string m_MotionLog; std::string m_SpikeLog; // signal generation FiberBundleType m_FiberBundleTransformed; ///< transformed bundle simulating headmotion itk::Vector m_WorkingSpacing; itk::Point m_WorkingOrigin; ImageRegion<3> m_WorkingImageRegion; double m_VoxelVolume; std::vector< DoubleDwiType::Pointer > m_CompartmentImages; ItkUcharImgType::Pointer m_TransformedMaskImage; ///< copy of mask image (changes for each motion step) ItkUcharImgType::Pointer m_UpsampledMaskImage; ///< helper image for motion simulation std::vector< MatrixType > m_RotationsInv; std::vector< MatrixType > m_Rotations; /// m_Translations; ///::Pointer m_DoubleInterpolator; itk::Vector m_NullDir; + + Float2DImageType::Pointer m_TickImage; + Float2DImageType::Pointer m_RfImage; }; } #ifndef ITK_MANUAL_INSTANTIATION #include "itkTractsToDWIImageFilter.cpp" #endif #endif diff --git a/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.cpp b/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.cpp index 89b8de3b36..07396322dd 100644 --- a/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.cpp +++ b/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.cpp @@ -1,1083 +1,1083 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #define RAPIDXML_NO_EXCEPTIONS #include #include #include #include #include #include #include #include #include mitk::FiberfoxParameters::FiberfoxParameters() : m_NoiseModel(nullptr) { mitk::StickModel* model_aniso = new mitk::StickModel(); model_aniso->m_CompartmentId = 1; m_FiberModelList.push_back(model_aniso); mitk::BallModel* model_iso = new mitk::BallModel(); model_iso->m_CompartmentId = 3; m_NonFiberModelList.push_back(model_iso); } mitk::FiberfoxParameters::FiberfoxParameters(const mitk::FiberfoxParameters& params) : m_NoiseModel(nullptr) { m_FiberGen = params.m_FiberGen; m_SignalGen = params.m_SignalGen; m_Misc = params.m_Misc; if (params.m_NoiseModel!=nullptr) { if (dynamic_cast*>(params.m_NoiseModel.get())) m_NoiseModel = std::make_shared< mitk::RicianNoiseModel<> >(); else if (dynamic_cast*>(params.m_NoiseModel.get())) m_NoiseModel = std::make_shared< mitk::ChiSquareNoiseModel<> >(); m_NoiseModel->SetNoiseVariance(params.m_NoiseModel->GetNoiseVariance()); } for (unsigned int i=0; i* outModel = nullptr; mitk::DiffusionSignalModel<>* signalModel = nullptr; if (i*>(signalModel)) outModel = new mitk::StickModel<>(dynamic_cast*>(signalModel)); else if (dynamic_cast*>(signalModel)) outModel = new mitk::TensorModel<>(dynamic_cast*>(signalModel)); else if (dynamic_cast*>(signalModel)) outModel = new mitk::RawShModel<>(dynamic_cast*>(signalModel)); else if (dynamic_cast*>(signalModel)) outModel = new mitk::BallModel<>(dynamic_cast*>(signalModel)); else if (dynamic_cast*>(signalModel)) outModel = new mitk::AstroStickModel<>(dynamic_cast*>(signalModel)); else if (dynamic_cast*>(signalModel)) outModel = new mitk::DotModel<>(dynamic_cast*>(signalModel)); if (i theta; theta.set_size(NPoints); vnl_vector phi; phi.set_size(NPoints); double C = sqrt(4*itk::Math::pi); phi(0) = 0.0; phi(NPoints-1) = 0.0; for(int i=0; i0 && i mitk::SignalGenerationParameters::GetBaselineIndices() { std::vector< int > result; for( unsigned int i=0; im_GradientDirections.size(); i++) if (m_GradientDirections.at(i).GetNorm()<0.0001) result.push_back(i); return result; } unsigned int mitk::SignalGenerationParameters::GetFirstBaselineIndex() { for( unsigned int i=0; im_GradientDirections.size(); i++) if (m_GradientDirections.at(i).GetNorm()<0.0001) return i; return -1; } bool mitk::SignalGenerationParameters::IsBaselineIndex(unsigned int idx) { if (m_GradientDirections.size()>idx && m_GradientDirections.at(idx).GetNorm()<0.0001) return true; return false; } unsigned int mitk::SignalGenerationParameters::GetNumWeightedVolumes() { return m_NumGradients; } unsigned int mitk::SignalGenerationParameters::GetNumBaselineVolumes() { return m_NumBaseline; } unsigned int mitk::SignalGenerationParameters::GetNumVolumes() { return m_GradientDirections.size(); } mitk::SignalGenerationParameters::GradientListType mitk::SignalGenerationParameters::GetGradientDirections() { return m_GradientDirections; } mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer mitk::SignalGenerationParameters::GetItkGradientContainer() { int c = 0; mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer out = mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::New(); for (auto g : m_GradientDirections) { mitk::DiffusionPropertyHelper::GradientDirectionType vnl_dir; vnl_dir[0] = g[0]; vnl_dir[1] = g[1]; vnl_dir[2] = g[2]; out->InsertElement(c, vnl_dir); ++c; } return out; } mitk::SignalGenerationParameters::GradientType mitk::SignalGenerationParameters::GetGradientDirection(unsigned int i) { return m_GradientDirections.at(i); } void mitk::SignalGenerationParameters::SetNumWeightedVolumes(int numGradients) { m_NumGradients = numGradients; GenerateGradientHalfShell(); } std::vector< int > mitk::SignalGenerationParameters::GetBvalues() { std::vector< int > bVals; for( GradientType g : m_GradientDirections) { float norm = g.GetNorm(); int bVal = std::round(norm*norm*m_Bvalue); if ( std::find(bVals.begin(), bVals.end(), bVal) == bVals.end() ) bVals.push_back(bVal); } return bVals; } double mitk::SignalGenerationParameters::GetBvalue() { return m_Bvalue; } void mitk::SignalGenerationParameters::SetGradienDirections(GradientListType gradientList) { m_GradientDirections = gradientList; m_NumGradients = 0; m_NumBaseline = 0; for( unsigned int i=0; im_GradientDirections.size(); i++) { float norm = m_GradientDirections.at(i).GetNorm(); if (norm>0.0001) m_NumGradients++; else m_NumBaseline++; } } void mitk::SignalGenerationParameters::SetGradienDirections(mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer gradientList) { m_NumGradients = 0; m_NumBaseline = 0; m_GradientDirections.clear(); for( unsigned int i=0; iSize(); i++) { GradientType g; g[0] = gradientList->at(i)[0]; g[1] = gradientList->at(i)[1]; g[2] = gradientList->at(i)[2]; m_GradientDirections.push_back(g); float norm = m_GradientDirections.at(i).GetNorm(); if (norm>0.0001) m_NumGradients++; else m_NumBaseline++; } } void mitk::SignalGenerationParameters::ApplyDirectionMatrix() { auto imageRotationMatrix = m_ImageDirection.GetVnlMatrix(); GradientListType rotated_gradients; for(auto g : m_GradientDirections) { vnl_vector vec = g.GetVnlVector(); vec = vec.pre_multiply(imageRotationMatrix); GradientType g2; g2[0] = vec[0]; g2[1] = vec[1]; g2[2] = vec[2]; rotated_gradients.push_back(g2); } m_GradientDirections = rotated_gradients; } void mitk::FiberfoxParameters::ApplyDirectionMatrix() { m_SignalGen.ApplyDirectionMatrix(); UpdateSignalModels(); } void mitk::FiberfoxParameters::SaveParameters(std::string filename) { if(filename.empty()) return; if(".ffp"!=filename.substr(filename.size()-4, 4)) filename += ".ffp"; const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, nullptr ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } boost::property_tree::ptree parameters; // fiber generation parameters parameters.put("fiberfox.fibers.distribution", m_FiberGen.m_Distribution); parameters.put("fiberfox.fibers.variance", m_FiberGen.m_Variance); parameters.put("fiberfox.fibers.density", m_FiberGen.m_Density); parameters.put("fiberfox.fibers.spline.sampling", m_FiberGen.m_Sampling); parameters.put("fiberfox.fibers.spline.tension", m_FiberGen.m_Tension); parameters.put("fiberfox.fibers.spline.continuity", m_FiberGen.m_Continuity); parameters.put("fiberfox.fibers.spline.bias", m_FiberGen.m_Bias); parameters.put("fiberfox.fibers.rotation.x", m_FiberGen.m_Rotation[0]); parameters.put("fiberfox.fibers.rotation.y", m_FiberGen.m_Rotation[1]); parameters.put("fiberfox.fibers.rotation.z", m_FiberGen.m_Rotation[2]); parameters.put("fiberfox.fibers.translation.x", m_FiberGen.m_Translation[0]); parameters.put("fiberfox.fibers.translation.y", m_FiberGen.m_Translation[1]); parameters.put("fiberfox.fibers.translation.z", m_FiberGen.m_Translation[2]); parameters.put("fiberfox.fibers.scale.x", m_FiberGen.m_Scale[0]); parameters.put("fiberfox.fibers.scale.y", m_FiberGen.m_Scale[1]); parameters.put("fiberfox.fibers.scale.z", m_FiberGen.m_Scale[2]); // image generation parameters parameters.put("fiberfox.image.basic.size.x", m_SignalGen.m_ImageRegion.GetSize(0)); parameters.put("fiberfox.image.basic.size.y", m_SignalGen.m_ImageRegion.GetSize(1)); parameters.put("fiberfox.image.basic.size.z", m_SignalGen.m_ImageRegion.GetSize(2)); parameters.put("fiberfox.image.basic.spacing.x", m_SignalGen.m_ImageSpacing[0]); parameters.put("fiberfox.image.basic.spacing.y", m_SignalGen.m_ImageSpacing[1]); parameters.put("fiberfox.image.basic.spacing.z", m_SignalGen.m_ImageSpacing[2]); parameters.put("fiberfox.image.basic.origin.x", m_SignalGen.m_ImageOrigin[0]); parameters.put("fiberfox.image.basic.origin.y", m_SignalGen.m_ImageOrigin[1]); parameters.put("fiberfox.image.basic.origin.z", m_SignalGen.m_ImageOrigin[2]); parameters.put("fiberfox.image.basic.direction.d1", m_SignalGen.m_ImageDirection[0][0]); parameters.put("fiberfox.image.basic.direction.d2", m_SignalGen.m_ImageDirection[0][1]); parameters.put("fiberfox.image.basic.direction.d3", m_SignalGen.m_ImageDirection[0][2]); parameters.put("fiberfox.image.basic.direction.d4", m_SignalGen.m_ImageDirection[1][0]); parameters.put("fiberfox.image.basic.direction.d5", m_SignalGen.m_ImageDirection[1][1]); parameters.put("fiberfox.image.basic.direction.d6", m_SignalGen.m_ImageDirection[1][2]); parameters.put("fiberfox.image.basic.direction.d7", m_SignalGen.m_ImageDirection[2][0]); parameters.put("fiberfox.image.basic.direction.d8", m_SignalGen.m_ImageDirection[2][1]); parameters.put("fiberfox.image.basic.direction.d9", m_SignalGen.m_ImageDirection[2][2]); mitk::gradients::WriteBvalsBvecs(filename+".bvals", filename+".bvecs", m_SignalGen.GetItkGradientContainer(), m_SignalGen.m_Bvalue); parameters.put("fiberfox.image.acquisitiontype", m_SignalGen.m_AcquisitionType); parameters.put("fiberfox.image.coilsensitivityprofile", m_SignalGen.m_CoilSensitivityProfile); parameters.put("fiberfox.image.numberofcoils", m_SignalGen.m_NumberOfCoils); parameters.put("fiberfox.image.reversephase", m_SignalGen.m_ReversePhase); parameters.put("fiberfox.image.partialfourier", m_SignalGen.m_PartialFourier); parameters.put("fiberfox.image.noisevariance", m_SignalGen.m_NoiseVariance); parameters.put("fiberfox.image.trep", m_SignalGen.m_tRep); parameters.put("fiberfox.image.tinv", m_SignalGen.m_tInv); parameters.put("fiberfox.image.signalScale", m_SignalGen.m_SignalScale); parameters.put("fiberfox.image.tEcho", m_SignalGen.m_tEcho); parameters.put("fiberfox.image.tLine", m_SignalGen.m_tLine); parameters.put("fiberfox.image.tInhom", m_SignalGen.m_tInhom); parameters.put("fiberfox.image.echoTrainLength", m_SignalGen.m_EchoTrainLength); parameters.put("fiberfox.image.simulatekspace", m_SignalGen.m_SimulateKspaceAcquisition); parameters.put("fiberfox.image.axonRadius", m_SignalGen.m_AxonRadius); parameters.put("fiberfox.image.doSimulateRelaxation", m_SignalGen.m_DoSimulateRelaxation); parameters.put("fiberfox.image.doDisablePartialVolume", m_SignalGen.m_DoDisablePartialVolume); parameters.put("fiberfox.image.artifacts.spikesnum", m_SignalGen.m_Spikes); parameters.put("fiberfox.image.artifacts.spikesscale", m_SignalGen.m_SpikeAmplitude); parameters.put("fiberfox.image.artifacts.kspaceLineOffset", m_SignalGen.m_KspaceLineOffset); parameters.put("fiberfox.image.artifacts.eddyStrength", m_SignalGen.m_EddyStrength); parameters.put("fiberfox.image.artifacts.eddyTau", m_SignalGen.m_Tau); parameters.put("fiberfox.image.artifacts.aliasingfactor", m_SignalGen.m_CroppingFactor); parameters.put("fiberfox.image.artifacts.drift", m_SignalGen.m_Drift); parameters.put("fiberfox.image.artifacts.doAddMotion", m_SignalGen.m_DoAddMotion); parameters.put("fiberfox.image.artifacts.randomMotion", m_SignalGen.m_DoRandomizeMotion); parameters.put("fiberfox.image.artifacts.translation0", m_SignalGen.m_Translation[0]); parameters.put("fiberfox.image.artifacts.translation1", m_SignalGen.m_Translation[1]); parameters.put("fiberfox.image.artifacts.translation2", m_SignalGen.m_Translation[2]); parameters.put("fiberfox.image.artifacts.rotation0", m_SignalGen.m_Rotation[0]); parameters.put("fiberfox.image.artifacts.rotation1", m_SignalGen.m_Rotation[1]); parameters.put("fiberfox.image.artifacts.rotation2", m_SignalGen.m_Rotation[2]); parameters.put("fiberfox.image.artifacts.motionvolumes", m_Misc.m_MotionVolumesBox); parameters.put("fiberfox.image.artifacts.addringing", m_SignalGen.m_DoAddGibbsRinging); parameters.put("fiberfox.image.artifacts.zeroringing", m_SignalGen.m_ZeroRinging); parameters.put("fiberfox.image.artifacts.addnoise", m_Misc.m_DoAddNoise); parameters.put("fiberfox.image.artifacts.addghosts", m_Misc.m_DoAddGhosts); parameters.put("fiberfox.image.artifacts.addaliasing", m_Misc.m_DoAddAliasing); parameters.put("fiberfox.image.artifacts.addspikes", m_Misc.m_DoAddSpikes); parameters.put("fiberfox.image.artifacts.addeddycurrents", m_Misc.m_DoAddEddyCurrents); parameters.put("fiberfox.image.artifacts.doAddDistortions", m_Misc.m_DoAddDistortions); parameters.put("fiberfox.image.artifacts.doAddDrift", m_SignalGen.m_DoAddDrift); - parameters.put("fiberfox.image.outputvolumefractions", m_Misc.m_CheckOutputVolumeFractionsBox); + parameters.put("fiberfox.image.outputvolumefractions", m_Misc.m_OutputAdditionalImages); parameters.put("fiberfox.image.showadvanced", m_Misc.m_CheckAdvancedSignalOptionsBox); parameters.put("fiberfox.image.signalmodelstring", m_Misc.m_SignalModelString); parameters.put("fiberfox.image.artifactmodelstring", m_Misc.m_ArtifactModelString); parameters.put("fiberfox.image.outpath", m_Misc.m_OutputPath); parameters.put("fiberfox.fibers.realtime", m_Misc.m_CheckRealTimeFibersBox); parameters.put("fiberfox.fibers.showadvanced", m_Misc.m_CheckAdvancedFiberOptionsBox); parameters.put("fiberfox.fibers.constantradius", m_Misc.m_CheckConstantRadiusBox); parameters.put("fiberfox.fibers.includeFiducials", m_Misc.m_CheckIncludeFiducialsBox); if (m_NoiseModel!=nullptr) { parameters.put("fiberfox.image.artifacts.noisevariance", m_NoiseModel->GetNoiseVariance()); if (dynamic_cast*>(m_NoiseModel.get())) parameters.put("fiberfox.image.artifacts.noisetype", "rice"); else if (dynamic_cast*>(m_NoiseModel.get())) parameters.put("fiberfox.image.artifacts.noisetype", "chisquare"); } for (std::size_t i=0; i* signalModel = nullptr; if (i(i)+".type", "fiber"); } else { signalModel = m_NonFiberModelList.at(i-m_FiberModelList.size()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".type", "non-fiber"); } if (dynamic_cast*>(signalModel)) { mitk::StickModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".model", "stick"); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".d", model->GetDiffusivity()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".t1", model->GetT1()); } else if (dynamic_cast*>(signalModel)) { mitk::TensorModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".model", "tensor"); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".d1", model->GetDiffusivity1()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".d2", model->GetDiffusivity2()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".d3", model->GetDiffusivity3()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".t1", model->GetT1()); } else if (dynamic_cast*>(signalModel)) { mitk::RawShModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".model", "prototype"); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".minFA", model->GetFaRange().first); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".maxFA", model->GetFaRange().second); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".minADC", model->GetAdcRange().first); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".maxADC", model->GetAdcRange().second); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".maxNumSamples", model->GetMaxNumKernels()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".numSamples", model->GetNumberOfKernels()); int shOrder = model->GetShOrder(); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".numCoeffs", (shOrder*shOrder + shOrder + 2)/2 + shOrder); for (unsigned int j=0; jGetNumberOfKernels(); j++) { vnl_vector< double > coeffs = model->GetCoefficients(j); for (unsigned int k=0; k(i)+".kernels."+boost::lexical_cast(j)+".coeffs."+boost::lexical_cast(k), coeffs[k]); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".kernels."+boost::lexical_cast(j)+".B0", model->GetBaselineSignal(j)); } } else if (dynamic_cast*>(signalModel)) { mitk::BallModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".model", "ball"); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".d", model->GetDiffusivity()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".t1", model->GetT1()); } else if (dynamic_cast*>(signalModel)) { mitk::AstroStickModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".model", "astrosticks"); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".d", model->GetDiffusivity()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".t1", model->GetT1()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".randomize", model->GetRandomizeSticks()); } else if (dynamic_cast*>(signalModel)) { mitk::DotModel<>* model = dynamic_cast*>(signalModel); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".model", "dot"); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".t2", model->GetT2()); parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".t1", model->GetT1()); } if (signalModel!=nullptr) { parameters.put("fiberfox.image.compartments.c"+boost::lexical_cast(i)+".ID", signalModel->m_CompartmentId); if (signalModel->GetVolumeFractionImage().IsNotNull()) { try{ itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); writer->SetFileName(filename+"_VOLUME"+boost::lexical_cast(signalModel->m_CompartmentId)+".nii.gz"); writer->SetInput(signalModel->GetVolumeFractionImage()); writer->Update(); MITK_INFO << "Volume fraction image for compartment "+boost::lexical_cast(signalModel->m_CompartmentId)+" saved."; } catch(...) { } } } } boost::property_tree::xml_writer_settings writerSettings(' ', 2); boost::property_tree::xml_parser::write_xml(filename, parameters, std::locale(), writerSettings); try{ itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); writer->SetFileName(filename+"_FMAP.nii.gz"); writer->SetInput(m_SignalGen.m_FrequencyMap); writer->Update(); } catch(...) { MITK_INFO << "No frequency map saved."; } try{ itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); writer->SetFileName(filename+"_MASK.nii.gz"); writer->SetInput(m_SignalGen.m_MaskImage); writer->Update(); } catch(...) { MITK_INFO << "No mask image saved."; } setlocale(LC_ALL, currLocale.c_str()); } template< class ParameterType > ParameterType mitk::FiberfoxParameters::ReadVal(boost::property_tree::ptree::value_type const& v, std::string tag, ParameterType defaultValue, bool essential) { try { return v.second.get(tag); } catch (...) { if (essential) { mitkThrow() << "Parameter file corrupted. Essential tag is missing: '" << tag << "'"; } if (tag!="artifacts.noisetype") { MITK_INFO << "Tag '" << tag << "' not found. Using default value '" << defaultValue << "'."; m_MissingTags += "\n- "; m_MissingTags += tag; } return defaultValue; } } void mitk::FiberfoxParameters::UpdateSignalModels() { for (mitk::DiffusionSignalModel<>* m : m_FiberModelList) { m->SetGradientList(m_SignalGen.m_GradientDirections); m->SetBvalue(m_SignalGen.m_Bvalue); } for (mitk::DiffusionSignalModel<>* m : m_NonFiberModelList) { m->SetGradientList(m_SignalGen.m_GradientDirections); m->SetBvalue(m_SignalGen.m_Bvalue); } } void mitk::FiberfoxParameters::SetNumWeightedVolumes(int numGradients) { m_SignalGen.SetNumWeightedVolumes(numGradients); UpdateSignalModels(); } void mitk::FiberfoxParameters::SetGradienDirections(mitk::SignalGenerationParameters::GradientListType gradientList) { m_SignalGen.SetGradienDirections(gradientList); UpdateSignalModels(); } void mitk::FiberfoxParameters::SetGradienDirections(mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer gradientList) { m_SignalGen.SetGradienDirections(gradientList); UpdateSignalModels(); } void mitk::FiberfoxParameters::SetBvalue(double Bvalue) { m_SignalGen.m_Bvalue = Bvalue; UpdateSignalModels(); } void mitk::FiberfoxParameters::GenerateGradientHalfShell() { m_SignalGen.GenerateGradientHalfShell(); UpdateSignalModels(); } void mitk::FiberfoxParameters::LoadParameters(std::string filename, bool fix_seed) { itk::Statistics::MersenneTwisterRandomVariateGenerator::Pointer randgen = itk::Statistics::MersenneTwisterRandomVariateGenerator::New(); if (fix_seed) { srand(0); randgen->SetSeed(0); } else { srand(time(0)); randgen->SetSeed(); } m_MissingTags = ""; if(filename.empty()) { return; } const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, nullptr ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } boost::property_tree::ptree parameterTree; boost::property_tree::xml_parser::read_xml( filename, parameterTree ); m_FiberModelList.clear(); m_NonFiberModelList.clear(); if (m_NoiseModel) { m_NoiseModel = nullptr; } BOOST_FOREACH( boost::property_tree::ptree::value_type const& v1, parameterTree.get_child("fiberfox") ) { if( v1.first == "fibers" ) { m_Misc.m_CheckRealTimeFibersBox = ReadVal(v1,"realtime", m_Misc.m_CheckRealTimeFibersBox); m_Misc.m_CheckAdvancedFiberOptionsBox = ReadVal(v1,"showadvanced", m_Misc.m_CheckAdvancedFiberOptionsBox); m_Misc.m_CheckConstantRadiusBox = ReadVal(v1,"constantradius", m_Misc.m_CheckConstantRadiusBox); m_Misc.m_CheckIncludeFiducialsBox = ReadVal(v1,"includeFiducials", m_Misc.m_CheckIncludeFiducialsBox); switch (ReadVal(v1,"distribution", 0)) { case 0: m_FiberGen.m_Distribution = FiberGenerationParameters::DISTRIBUTE_UNIFORM; break; case 1: m_FiberGen.m_Distribution = FiberGenerationParameters::DISTRIBUTE_GAUSSIAN; break; default: m_FiberGen.m_Distribution = FiberGenerationParameters::DISTRIBUTE_UNIFORM; } m_FiberGen.m_Variance = ReadVal(v1,"variance", m_FiberGen.m_Variance); m_FiberGen.m_Density = ReadVal(v1,"density", m_FiberGen.m_Density); m_FiberGen.m_Sampling = ReadVal(v1,"spline.sampling", m_FiberGen.m_Sampling); m_FiberGen.m_Tension = ReadVal(v1,"spline.tension", m_FiberGen.m_Tension); m_FiberGen.m_Continuity = ReadVal(v1,"spline.continuity", m_FiberGen.m_Continuity); m_FiberGen.m_Bias = ReadVal(v1,"spline.bias", m_FiberGen.m_Bias); m_FiberGen.m_Rotation[0] = ReadVal(v1,"rotation.x", m_FiberGen.m_Rotation[0]); m_FiberGen.m_Rotation[1] = ReadVal(v1,"rotation.y", m_FiberGen.m_Rotation[1]); m_FiberGen.m_Rotation[2] = ReadVal(v1,"rotation.z", m_FiberGen.m_Rotation[2]); m_FiberGen.m_Translation[0] = ReadVal(v1,"translation.x", m_FiberGen.m_Translation[0]); m_FiberGen.m_Translation[1] = ReadVal(v1,"translation.y", m_FiberGen.m_Translation[1]); m_FiberGen.m_Translation[2] = ReadVal(v1,"translation.z", m_FiberGen.m_Translation[2]); m_FiberGen.m_Scale[0] = ReadVal(v1,"scale.x", m_FiberGen.m_Scale[0]); m_FiberGen.m_Scale[1] = ReadVal(v1,"scale.y", m_FiberGen.m_Scale[1]); m_FiberGen.m_Scale[2] = ReadVal(v1,"scale.z", m_FiberGen.m_Scale[2]); } else if ( v1.first == "image" ) { m_Misc.m_SignalModelString = ReadVal(v1,"signalmodelstring", m_Misc.m_SignalModelString); m_Misc.m_ArtifactModelString = ReadVal(v1,"artifactmodelstring", m_Misc.m_ArtifactModelString); m_Misc.m_OutputPath = ReadVal(v1,"outpath", m_Misc.m_OutputPath); - m_Misc.m_CheckOutputVolumeFractionsBox = ReadVal(v1,"outputvolumefractions", m_Misc.m_CheckOutputVolumeFractionsBox); + m_Misc.m_OutputAdditionalImages = ReadVal(v1,"outputvolumefractions", m_Misc.m_OutputAdditionalImages); m_Misc.m_CheckAdvancedSignalOptionsBox = ReadVal(v1,"showadvanced", m_Misc.m_CheckAdvancedSignalOptionsBox); m_Misc.m_DoAddDistortions = ReadVal(v1,"artifacts.doAddDistortions", m_Misc.m_DoAddDistortions); m_Misc.m_DoAddNoise = ReadVal(v1,"artifacts.addnoise", m_Misc.m_DoAddNoise); m_Misc.m_DoAddGhosts = ReadVal(v1,"artifacts.addghosts", m_Misc.m_DoAddGhosts); m_Misc.m_DoAddAliasing = ReadVal(v1,"artifacts.addaliasing", m_Misc.m_DoAddAliasing); m_Misc.m_DoAddSpikes = ReadVal(v1,"artifacts.addspikes", m_Misc.m_DoAddSpikes); m_Misc.m_DoAddEddyCurrents = ReadVal(v1,"artifacts.addeddycurrents", m_Misc.m_DoAddEddyCurrents); m_SignalGen.m_ImageRegion.SetSize(0, ReadVal(v1,"basic.size.x",m_SignalGen.m_ImageRegion.GetSize(0))); m_SignalGen.m_ImageRegion.SetSize(1, ReadVal(v1,"basic.size.y",m_SignalGen.m_ImageRegion.GetSize(1))); m_SignalGen.m_ImageRegion.SetSize(2, ReadVal(v1,"basic.size.z",m_SignalGen.m_ImageRegion.GetSize(2))); m_SignalGen.m_ImageSpacing[0] = ReadVal(v1,"basic.spacing.x",m_SignalGen.m_ImageSpacing[0]); m_SignalGen.m_ImageSpacing[1] = ReadVal(v1,"basic.spacing.y",m_SignalGen.m_ImageSpacing[1]); m_SignalGen.m_ImageSpacing[2] = ReadVal(v1,"basic.spacing.z",m_SignalGen.m_ImageSpacing[2]); m_SignalGen.m_ImageOrigin[0] = ReadVal(v1,"basic.origin.x",m_SignalGen.m_ImageOrigin[0]); m_SignalGen.m_ImageOrigin[1] = ReadVal(v1,"basic.origin.y",m_SignalGen.m_ImageOrigin[1]); m_SignalGen.m_ImageOrigin[2] = ReadVal(v1,"basic.origin.z",m_SignalGen.m_ImageOrigin[2]); int i = 0; int j = 0; for(auto v : v1.second.get_child("basic.direction")) { m_SignalGen.m_ImageDirection[i][j] = boost::lexical_cast(v.second.data()); ++j; if (j==3) { j = 0; ++i; } } m_SignalGen.m_AcquisitionType = (SignalGenerationParameters::AcquisitionType)ReadVal(v1,"acquisitiontype", m_SignalGen.m_AcquisitionType); m_SignalGen.m_CoilSensitivityProfile = (SignalGenerationParameters::CoilSensitivityProfile)ReadVal(v1,"coilsensitivityprofile", m_SignalGen.m_CoilSensitivityProfile); m_SignalGen.m_NumberOfCoils = ReadVal(v1,"numberofcoils", m_SignalGen.m_NumberOfCoils); m_SignalGen.m_ReversePhase = ReadVal(v1,"reversephase", m_SignalGen.m_ReversePhase); m_SignalGen.m_PartialFourier = ReadVal(v1,"partialfourier", m_SignalGen.m_PartialFourier); m_SignalGen.m_NoiseVariance = ReadVal(v1,"noisevariance", m_SignalGen.m_NoiseVariance); m_SignalGen.m_tRep = ReadVal(v1,"trep", m_SignalGen.m_tRep); m_SignalGen.m_tInv = ReadVal(v1,"tinv", m_SignalGen.m_tInv); m_SignalGen.m_SignalScale = ReadVal(v1,"signalScale", m_SignalGen.m_SignalScale); m_SignalGen.m_tEcho = ReadVal(v1,"tEcho", m_SignalGen.m_tEcho); m_SignalGen.m_tLine = ReadVal(v1,"tLine", m_SignalGen.m_tLine); m_SignalGen.m_tInhom = ReadVal(v1,"tInhom", m_SignalGen.m_tInhom); m_SignalGen.m_EchoTrainLength = ReadVal(v1,"echoTrainLength", m_SignalGen.m_EchoTrainLength); m_SignalGen.m_SimulateKspaceAcquisition = ReadVal(v1,"simulatekspace", m_SignalGen.m_SimulateKspaceAcquisition); m_SignalGen.m_AxonRadius = ReadVal(v1,"axonRadius", m_SignalGen.m_AxonRadius); m_SignalGen.m_Spikes = ReadVal(v1,"artifacts.spikesnum", m_SignalGen.m_Spikes); m_SignalGen.m_SpikeAmplitude = ReadVal(v1,"artifacts.spikesscale", m_SignalGen.m_SpikeAmplitude); m_SignalGen.m_KspaceLineOffset = ReadVal(v1,"artifacts.kspaceLineOffset", m_SignalGen.m_KspaceLineOffset); m_SignalGen.m_EddyStrength = ReadVal(v1,"artifacts.eddyStrength", m_SignalGen.m_EddyStrength); m_SignalGen.m_Tau = ReadVal(v1,"artifacts.eddyTau", m_SignalGen.m_Tau); m_SignalGen.m_CroppingFactor = ReadVal(v1,"artifacts.aliasingfactor", m_SignalGen.m_CroppingFactor); m_SignalGen.m_Drift = ReadVal(v1,"artifacts.drift", m_SignalGen.m_Drift); m_SignalGen.m_DoAddGibbsRinging = ReadVal(v1,"artifacts.addringing", m_SignalGen.m_DoAddGibbsRinging); m_SignalGen.m_ZeroRinging = ReadVal(v1,"artifacts.zeroringing", m_SignalGen.m_ZeroRinging); m_SignalGen.m_DoSimulateRelaxation = ReadVal(v1,"doSimulateRelaxation", m_SignalGen.m_DoSimulateRelaxation); m_SignalGen.m_DoDisablePartialVolume = ReadVal(v1,"doDisablePartialVolume", m_SignalGen.m_DoDisablePartialVolume); m_SignalGen.m_DoAddMotion = ReadVal(v1,"artifacts.doAddMotion", m_SignalGen.m_DoAddMotion); m_SignalGen.m_DoRandomizeMotion = ReadVal(v1,"artifacts.randomMotion", m_SignalGen.m_DoRandomizeMotion); m_SignalGen.m_DoAddDrift = ReadVal(v1,"artifacts.doAddDrift", m_SignalGen.m_DoAddDrift); m_SignalGen.m_Translation[0] = ReadVal(v1,"artifacts.translation0", m_SignalGen.m_Translation[0]); m_SignalGen.m_Translation[1] = ReadVal(v1,"artifacts.translation1", m_SignalGen.m_Translation[1]); m_SignalGen.m_Translation[2] = ReadVal(v1,"artifacts.translation2", m_SignalGen.m_Translation[2]); m_SignalGen.m_Rotation[0] = ReadVal(v1,"artifacts.rotation0", m_SignalGen.m_Rotation[0]); m_SignalGen.m_Rotation[1] = ReadVal(v1,"artifacts.rotation1", m_SignalGen.m_Rotation[1]); m_SignalGen.m_Rotation[2] = ReadVal(v1,"artifacts.rotation2", m_SignalGen.m_Rotation[2]); if (itksys::SystemTools::FileExists(filename+".bvals") && itksys::SystemTools::FileExists(filename+".bvecs")) { m_Misc.m_BvalsFile = filename+".bvals"; m_Misc.m_BvecsFile = filename+".bvecs"; m_SignalGen.SetGradienDirections( mitk::gradients::ReadBvalsBvecs(m_Misc.m_BvalsFile, m_Misc.m_BvecsFile, m_SignalGen.m_Bvalue) ); } else { m_SignalGen.m_Bvalue = ReadVal(v1,"bvalue", m_SignalGen.m_Bvalue); SignalGenerationParameters::GradientListType gradients; try { BOOST_FOREACH( boost::property_tree::ptree::value_type const& v2, v1.second.get_child("gradients") ) { SignalGenerationParameters::GradientType g; g[0] = ReadVal(v2,"x",0); g[1] = ReadVal(v2,"y",0); g[2] = ReadVal(v2,"z",0); gradients.push_back(g); } } catch(...) { MITK_INFO << "WARNING: Fiberfox parameters without any gradient directions loaded."; } m_SignalGen.SetGradienDirections(gradients); } m_Misc.m_MotionVolumesBox = ReadVal(v1,"artifacts.motionvolumes", m_Misc.m_MotionVolumesBox); m_SignalGen.m_MotionVolumes.clear(); if ( m_Misc.m_MotionVolumesBox == "random" ) { m_SignalGen.m_MotionVolumes.push_back(0); for ( size_t i=1; i < m_SignalGen.GetNumVolumes(); ++i ) { m_SignalGen.m_MotionVolumes.push_back( bool( randgen->GetIntegerVariate()%2 ) ); } MITK_DEBUG << "mitkFiberfoxParameters.cpp: Case m_Misc.m_MotionVolumesBox == \"random\"."; } else if ( ! m_Misc.m_MotionVolumesBox.empty() ) { std::stringstream stream( m_Misc.m_MotionVolumesBox ); std::vector numbers; int nummer = std::numeric_limits::max(); while( stream >> nummer ) { if( nummer < std::numeric_limits::max() ) { numbers.push_back( nummer ); } } // If a list of negative numbers is given: if( *(std::min_element( numbers.begin(), numbers.end() )) < 0 && *(std::max_element( numbers.begin(), numbers.end() )) <= 0 ) // cave: -0 == +0 { for ( size_t i=0; i(m_SignalGen.GetNumVolumes()) && -number >= 0 ) m_SignalGen.m_MotionVolumes.at(-number) = false; } MITK_DEBUG << "mitkFiberfoxParameters.cpp: Case list of negative numbers."; } // If a list of positive numbers is given: else if( *(std::min_element( numbers.begin(), numbers.end() )) >= 0 && *(std::max_element( numbers.begin(), numbers.end() )) >= 0 ) { for ( size_t i=0; i(m_SignalGen.GetNumVolumes()) && number >= 0) m_SignalGen.m_MotionVolumes.at(number) = true; } MITK_DEBUG << "mitkFiberfoxParameters.cpp: Case list of positive numbers."; } else { MITK_WARN << "mitkFiberfoxParameters.cpp: Inconsistent list of numbers in m_MotionVolumesBox."; break; } } else { MITK_WARN << "mitkFiberfoxParameters.cpp: Cannot make sense of string in m_MotionVolumesBox."; break; } try { if (ReadVal(v1,"artifacts.noisetype","")=="rice") { m_NoiseModel = std::make_shared< mitk::RicianNoiseModel<> >(); m_NoiseModel->SetNoiseVariance(ReadVal(v1,"artifacts.noisevariance",m_NoiseModel->GetNoiseVariance())); } } catch(...) { MITK_DEBUG << "mitkFiberfoxParameters.cpp: caught some error while trying m_NoiseModel->SetNoiseVariance()"; // throw; } try { if (ReadVal(v1,"artifacts.noisetype","")=="chisquare") { m_NoiseModel = std::make_shared< mitk::ChiSquareNoiseModel<> >(); m_NoiseModel->SetNoiseVariance(ReadVal(v1,"artifacts.noisevariance",m_NoiseModel->GetNoiseVariance())); } } catch(...) { MITK_DEBUG << "mitkFiberfoxParameters.cpp: caught some error while trying m_NoiseModel->SetNoiseVariance()"; // throw; } BOOST_FOREACH( boost::property_tree::ptree::value_type const& v2, v1.second.get_child("compartments") ) { mitk::DiffusionSignalModel<>* signalModel = nullptr; std::string model = ReadVal(v2,"model","",true); if (model=="stick") { mitk::StickModel<>* model = new mitk::StickModel<>(); model->SetDiffusivity(ReadVal(v2,"d",model->GetDiffusivity())); model->SetT2(ReadVal(v2,"t2",model->GetT2())); model->SetT1(ReadVal(v2,"t1",model->GetT1())); model->SetBvalue(m_SignalGen.m_Bvalue); model->m_CompartmentId = ReadVal(v2,"ID",0,true); if (ReadVal(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="tensor") { mitk::TensorModel<>* model = new mitk::TensorModel<>(); model->SetDiffusivity1(ReadVal(v2,"d1",model->GetDiffusivity1())); model->SetDiffusivity2(ReadVal(v2,"d2",model->GetDiffusivity2())); model->SetDiffusivity3(ReadVal(v2,"d3",model->GetDiffusivity3())); model->SetT2(ReadVal(v2,"t2",model->GetT2())); model->SetT1(ReadVal(v2,"t1",model->GetT1())); model->SetBvalue(m_SignalGen.m_Bvalue); model->m_CompartmentId = ReadVal(v2,"ID",0,true); if (ReadVal(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="ball") { mitk::BallModel<>* model = new mitk::BallModel<>(); model->SetDiffusivity(ReadVal(v2,"d",model->GetDiffusivity())); model->SetT2(ReadVal(v2,"t2",model->GetT2())); model->SetT1(ReadVal(v2,"t1",model->GetT1())); model->SetBvalue(m_SignalGen.m_Bvalue); model->m_CompartmentId = ReadVal(v2,"ID",0,true); if (ReadVal(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="astrosticks") { mitk::AstroStickModel<>* model = new AstroStickModel<>(); model->SetDiffusivity(ReadVal(v2,"d",model->GetDiffusivity())); model->SetT2(ReadVal(v2,"t2",model->GetT2())); model->SetT1(ReadVal(v2,"t1",model->GetT1())); model->SetBvalue(m_SignalGen.m_Bvalue); model->SetRandomizeSticks(ReadVal(v2,"randomize",model->GetRandomizeSticks())); model->m_CompartmentId = ReadVal(v2,"ID",0,true); if (ReadVal(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="dot") { mitk::DotModel<>* model = new mitk::DotModel<>(); model->SetT2(ReadVal(v2,"t2",model->GetT2())); model->SetT1(ReadVal(v2,"t1",model->GetT1())); model->m_CompartmentId = ReadVal(v2,"ID",0,true); if (ReadVal(v2,"type","",true)=="fiber") m_FiberModelList.push_back(model); else if (ReadVal(v2,"type","",true)=="non-fiber") m_NonFiberModelList.push_back(model); signalModel = model; } else if (model=="prototype") { mitk::RawShModel<>* model = new mitk::RawShModel<>(); model->SetMaxNumKernels(ReadVal(v2,"maxNumSamples",model->GetMaxNumKernels())); model->SetFaRange(ReadVal(v2,"minFA",model->GetFaRange().first), ReadVal(v2,"maxFA",model->GetFaRange().second)); model->SetAdcRange(ReadVal(v2,"minADC",model->GetAdcRange().first), ReadVal(v2,"maxADC",model->GetAdcRange().second)); model->m_CompartmentId = ReadVal(v2,"ID",0,true); unsigned int numCoeffs = ReadVal(v2,"numCoeffs",0,true); unsigned int numSamples = ReadVal(v2,"numSamples",0,true); for (unsigned int j=0; j coeffs(numCoeffs); for (unsigned int k=0; k(v2,"kernels."+boost::lexical_cast(j)+".coeffs."+boost::lexical_cast(k),0,true); } model->SetShCoefficients( coeffs, ReadVal(v2,"kernels."+boost::lexical_cast(j)+".B0",0,true) ); } if (ReadVal(v2,"type","",true)=="fiber") { m_FiberModelList.push_back(model); } else if (ReadVal(v2,"type","",true)=="non-fiber") { m_NonFiberModelList.push_back(model); } // else ? signalModel = model; } if (signalModel!=nullptr) { try { itk::ImageFileReader::Pointer reader = itk::ImageFileReader::New(); if ( itksys::SystemTools::FileExists(filename+"_VOLUME"+ReadVal(v2,"ID","")+".nii.gz") ) reader->SetFileName(filename+"_VOLUME"+ReadVal(v2,"ID","")+".nii.gz"); else if ( itksys::SystemTools::FileExists(filename+"_VOLUME"+ReadVal(v2,"ID","")+".nii") ) reader->SetFileName(filename+"_VOLUME"+ReadVal(v2,"ID","")+".nii"); else reader->SetFileName(filename+"_VOLUME"+ReadVal(v2,"ID","")+".nrrd"); reader->Update(); signalModel->SetVolumeFractionImage(reader->GetOutput()); MITK_INFO << "Volume fraction image loaded for compartment " << signalModel->m_CompartmentId; } catch(...) { MITK_INFO << "No volume fraction image found for compartment " << signalModel->m_CompartmentId; } } } } else { } } UpdateSignalModels(); try { itk::ImageFileReader::Pointer reader = itk::ImageFileReader::New(); reader->SetFileName(filename+"_FMAP.nrrd"); if ( itksys::SystemTools::FileExists(filename+"_FMAP.nii.gz") ) reader->SetFileName(filename+"_FMAP.nii.gz"); else if ( itksys::SystemTools::FileExists(filename+"_FMAP.nii") ) reader->SetFileName(filename+"_FMAP.nii"); else reader->SetFileName(filename+"_FMAP.nrrd"); reader->Update(); m_SignalGen.m_FrequencyMap = reader->GetOutput(); MITK_INFO << "Frequency map loaded."; } catch(...) { MITK_INFO << "No frequency map found."; } try { itk::ImageFileReader::Pointer reader = itk::ImageFileReader::New(); if ( itksys::SystemTools::FileExists(filename+"_MASK.nii.gz") ) reader->SetFileName(filename+"_MASK.nii.gz"); else if ( itksys::SystemTools::FileExists(filename+"_MASK.nii") ) reader->SetFileName(filename+"_MASK.nii"); else reader->SetFileName(filename+"_MASK.nrrd"); reader->Update(); m_SignalGen.m_MaskImage = reader->GetOutput(); m_SignalGen.m_ImageRegion = m_SignalGen.m_MaskImage->GetLargestPossibleRegion(); m_SignalGen.m_ImageSpacing = m_SignalGen.m_MaskImage->GetSpacing(); m_SignalGen.m_ImageOrigin = m_SignalGen.m_MaskImage->GetOrigin(); m_SignalGen.m_ImageDirection = m_SignalGen.m_MaskImage->GetDirection(); MITK_INFO << "Mask image loaded."; } catch(...) { MITK_INFO << "No mask image found."; } setlocale(LC_ALL, currLocale.c_str()); } void mitk::FiberfoxParameters::PrintSelf() { MITK_INFO << "Not implemented :("; } diff --git a/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.h b/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.h index 9345aff4f1..0aa0e29008 100644 --- a/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.h +++ b/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkFiberfoxParameters.h @@ -1,339 +1,339 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_FiberfoxParameters_H #define _MITK_FiberfoxParameters_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { class MITKFIBERTRACKING_EXPORT FiberfoxParameters; /** Signal generation */ class MITKFIBERTRACKING_EXPORT SignalGenerationParameters { friend FiberfoxParameters; public: typedef itk::Image ItkFloatImgType; typedef itk::Image ItkUcharImgType; typedef itk::Vector GradientType; typedef std::vector GradientListType; enum CoilSensitivityProfile : int { COIL_CONSTANT, COIL_LINEAR, COIL_EXPONENTIAL }; enum AcquisitionType : int { SingleShotEpi, ConventionalSpinEcho, FastSpinEcho, }; SignalGenerationParameters() : m_AcquisitionType(SignalGenerationParameters::SingleShotEpi) , m_SignalScale(100) , m_tEcho(100) , m_tRep(4000) , m_tInv(0) , m_tLine(1) , m_tInhom(50) , m_EchoTrainLength(8) , m_ReversePhase(false) , m_PartialFourier(1.0) , m_NoiseVariance(0.001f) , m_NumberOfCoils(1) , m_CoilSensitivityProfile(SignalGenerationParameters::COIL_CONSTANT) , m_CoilSensitivity(0.3f) , m_SimulateKspaceAcquisition(false) , m_AxonRadius(0) , m_DoDisablePartialVolume(false) , m_Spikes(0) , m_SpikeAmplitude(1) , m_KspaceLineOffset(0) , m_EddyStrength(0.002f) , m_Tau(70) , m_CroppingFactor(1) , m_Drift(0.06) , m_DoAddGibbsRinging(false) , m_ZeroRinging(0) , m_DoSimulateRelaxation(true) , m_DoAddMotion(false) , m_DoRandomizeMotion(true) , m_DoAddDrift(false) , m_FrequencyMap(nullptr) , m_MaskImage(nullptr) , m_Bvalue(1000) { m_ImageRegion.SetSize(0, 12); m_ImageRegion.SetSize(1, 12); m_ImageRegion.SetSize(2, 3); m_ImageSpacing.Fill(2.0); m_ImageOrigin.Fill(0.0); m_ImageDirection.SetIdentity(); m_Translation.Fill(0.0); m_Rotation.Fill(0.0); SetNumWeightedVolumes(6); } /** input/output image specifications */ itk::ImageRegion<3> m_CroppedRegion; ///< Image size with reduced FOV. itk::ImageRegion<3> m_ImageRegion; ///< Image size. itk::Vector m_ImageSpacing; ///< Image voxel size. itk::Point m_ImageOrigin; ///< Image origin. itk::Matrix m_ImageDirection; ///< Image rotation matrix. /** Other acquisitions parameters */ AcquisitionType m_AcquisitionType; ///< determines k-space trajectory and maximum echo position(s) float m_SignalScale; ///< Scaling factor for output signal (before noise is added). float m_tEcho; ///< Echo time TE. float m_tRep; ///< Echo time TR. float m_tInv; ///< Inversion time float m_tLine; ///< k-space line readout time (dwell time). float m_tInhom; ///< T2' unsigned int m_EchoTrainLength; ///< Only relevant for Fast Spin Echo sequence (number of k-space lines acquired with one RF pulse) bool m_ReversePhase; ///< If true, the phase readout direction will be inverted (-y instead of y) float m_PartialFourier; ///< Partial fourier factor (0.5-1) float m_NoiseVariance; ///< Variance of complex gaussian noise unsigned int m_NumberOfCoils; ///< Number of coils in multi-coil acquisition CoilSensitivityProfile m_CoilSensitivityProfile; ///< Choose between constant, linear or exponential sensitivity profile of the used coils float m_CoilSensitivity; ///< signal remaining in slice center bool m_SimulateKspaceAcquisition;///< Flag to enable/disable k-space acquisition simulation double m_AxonRadius; ///< Determines compartment volume fractions (0 == automatic axon radius estimation) bool m_DoDisablePartialVolume; ///< Disable partial volume effects. Each voxel is either all fiber or all non-fiber. /** Artifacts and other effects */ unsigned int m_Spikes; ///< Number of spikes randomly appearing in the image float m_SpikeAmplitude; ///< amplitude of spikes relative to the largest signal intensity (magnitude of complex) float m_KspaceLineOffset; ///< Causes N/2 ghosts. Larger offset means stronger ghost. float m_EddyStrength; ///< Strength of eddy current induced gradients in mT/m. float m_Tau; ///< Eddy current decay constant (in ms) float m_CroppingFactor; ///< FOV size in y-direction is multiplied by this factor. Causes aliasing artifacts. float m_Drift; ///< Global signal decrease by the end of the acquisition. bool m_DoAddGibbsRinging; ///< Add Gibbs ringing artifact int m_ZeroRinging; ///< If > 0, ringing is simulated by by setting the defined percentage of higher frequencies to 0 in k-space. Otherwise, the input to the k-space simulation is generated with twice the resolution and cropped during k-space simulation (much slower). bool m_DoSimulateRelaxation; ///< Add T2 relaxation effects bool m_DoAddMotion; ///< Enable motion artifacts. bool m_DoRandomizeMotion; ///< Toggles between random and linear motion. bool m_DoAddDrift; ///< Add quadratic signal drift. std::vector< bool > m_MotionVolumes; ///< Indicates the image volumes that are affected by motion ///< with positive numbers, inverted logic with negative numbers. itk::Vector m_Translation; ///< Maximum translational motion. itk::Vector m_Rotation; ///< Maximum rotational motion. ItkFloatImgType::Pointer m_FrequencyMap; ///< If != nullptr, distortions are added to the image using this frequency map. ItkUcharImgType::Pointer m_MaskImage; ///< Signal is only genrated inside of the mask image. std::vector< int > GetBaselineIndices(); ///< Returns list of nun-diffusion-weighted image volume indices unsigned int GetFirstBaselineIndex(); ///< Returns index of first non-diffusion-weighted image volume bool IsBaselineIndex(unsigned int idx); ///< Checks if image volume with given index is non-diffusion-weighted volume or not. unsigned int GetNumWeightedVolumes(); ///< Get number of diffusion-weighted image volumes unsigned int GetNumBaselineVolumes(); ///< Get number of non-diffusion-weighted image volumes unsigned int GetNumVolumes(); ///< Get number of baseline and diffusion-weighted image volumes GradientListType GetGradientDirections(); ///< Return gradient direction container mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer GetItkGradientContainer(); GradientType GetGradientDirection(unsigned int i); std::vector< int > GetBvalues(); ///< Returns a vector with all unique b-values (determined by the gradient magnitudes) double GetBvalue(); void ApplyDirectionMatrix(); protected: unsigned int m_NumGradients; ///< Number of diffusion-weighted image volumes. unsigned int m_NumBaseline; ///< Number of non-diffusion-weighted image volumes. GradientListType m_GradientDirections; ///< Total number of image volumes. double m_Bvalue; ///< Acquisition b-value void SetNumWeightedVolumes(int numGradients); ///< Automaticall calls GenerateGradientHalfShell() afterwards. void SetGradienDirections(GradientListType gradientList); void SetGradienDirections(mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer gradientList); void GenerateGradientHalfShell(); ///< Generates half shell of gradient directions (with m_NumGradients non-zero directions) }; /** Fiber generation */ class MITKFIBERTRACKING_EXPORT FiberGenerationParameters { public: enum FiberDistribution { DISTRIBUTE_UNIFORM, // distribute fibers uniformly in the ROIs DISTRIBUTE_GAUSSIAN // distribute fibers using a 2D gaussian }; typedef std::vector< std::vector< mitk::PlanarEllipse::Pointer > > FiducialListType; typedef std::vector< std::vector< unsigned int > > FlipListType; FiberGenerationParameters() : m_Distribution(DISTRIBUTE_UNIFORM) , m_Density(100) , m_Variance(100) , m_Sampling(1) , m_Tension(0) , m_Continuity(0) , m_Bias(0) { m_Rotation.Fill(0.0); m_Translation.Fill(0.0); m_Scale.Fill(1.0); } FiberDistribution m_Distribution; unsigned int m_Density; double m_Variance; double m_Sampling; double m_Tension; double m_Continuity; double m_Bias; mitk::Vector3D m_Rotation; mitk::Vector3D m_Translation; mitk::Vector3D m_Scale; FlipListType m_FlipList; ///< contains flags indicating a flip of the 2D fiber x-coordinates (needed to resolve some unwanted fiber twisting) FiducialListType m_Fiducials; ///< container of the planar ellipses used as fiducials for the fiber generation process }; /** GUI persistence, input, output, ... */ class MITKFIBERTRACKING_EXPORT MiscFiberfoxParameters { public: MiscFiberfoxParameters() : m_ResultNode(DataNode::New()) , m_ParentNode(nullptr) , m_SignalModelString("") , m_ArtifactModelString("") , m_OutputPath("/tmp/") , m_OutputPrefix("fiberfox") , m_AfterSimulationMessage("") , m_BvalsFile("") , m_BvecsFile("") - , m_CheckOutputVolumeFractionsBox(false) + , m_OutputAdditionalImages(false) , m_CheckAdvancedSignalOptionsBox(false) , m_DoAddNoise(false) , m_DoAddGhosts(false) , m_DoAddAliasing(false) , m_DoAddSpikes(false) , m_DoAddEddyCurrents(false) , m_DoAddDistortions(false) , m_MotionVolumesBox("random") , m_CheckRealTimeFibersBox(true) , m_CheckAdvancedFiberOptionsBox(false) , m_CheckConstantRadiusBox(false) , m_CheckIncludeFiducialsBox(true) {} DataNode::Pointer m_ResultNode; ///< Stores resulting image. DataNode::Pointer m_ParentNode; ///< Parent node of result node. std::string m_SignalModelString; ///< Appendet to the name of the result node std::string m_ArtifactModelString; ///< Appendet to the name of the result node std::string m_OutputPath; ///< Image is automatically saved to the specified folder after simulation is finished. std::string m_OutputPrefix; /** Prefix for filename of output files and logfile. */ std::string m_AfterSimulationMessage; ///< Store messages that are displayed after the simulation has finished (e.g. warnings, automatic parameter adjustments etc.) std::string m_BvalsFile; std::string m_BvecsFile; /** member variables that store the check-state of GUI checkboxes */ // image generation - bool m_CheckOutputVolumeFractionsBox; + bool m_OutputAdditionalImages; bool m_CheckAdvancedSignalOptionsBox; bool m_DoAddNoise; bool m_DoAddGhosts; bool m_DoAddAliasing; bool m_DoAddSpikes; bool m_DoAddEddyCurrents; bool m_DoAddDistortions; std::string m_MotionVolumesBox; // fiber generation bool m_CheckRealTimeFibersBox; bool m_CheckAdvancedFiberOptionsBox; bool m_CheckConstantRadiusBox; bool m_CheckIncludeFiducialsBox; }; /** * \brief Datastructure to manage the Fiberfox signal generation parameters. * */ class MITKFIBERTRACKING_EXPORT FiberfoxParameters { public: typedef itk::Image ItkFloatImgType; typedef itk::Image ItkDoubleImgType; typedef itk::Image ItkUcharImgType; typedef DiffusionSignalModel DiffusionModelType; typedef std::vector< DiffusionModelType* > DiffusionModelListType; typedef DiffusionNoiseModel NoiseModelType; FiberfoxParameters(); FiberfoxParameters(const FiberfoxParameters ¶ms); ~FiberfoxParameters(); /** Not templated parameters */ FiberGenerationParameters m_FiberGen; ///< Fiber generation parameters SignalGenerationParameters m_SignalGen; ///< Signal generation parameters MiscFiberfoxParameters m_Misc; ///< GUI realted and I/O parameters /** Templated parameters */ DiffusionModelListType m_FiberModelList; ///< Intra- and inter-axonal compartments. DiffusionModelListType m_NonFiberModelList; ///< Extra-axonal compartments. std::shared_ptr< NoiseModelType > m_NoiseModel; ///< If != nullptr, noise is added to the image. void GenerateGradientHalfShell(); void SetNumWeightedVolumes(int numGradients); ///< Automaticall calls GenerateGradientHalfShell() afterwards. void SetGradienDirections(mitk::SignalGenerationParameters::GradientListType gradientList); void SetGradienDirections(mitk::DiffusionPropertyHelper::GradientDirectionsContainerType::Pointer gradientList); void SetBvalue(double Bvalue); void UpdateSignalModels(); void ClearFiberParameters(); void ClearSignalParameters(); void ApplyDirectionMatrix(); void PrintSelf(); ///< Print parameters to stdout. void SaveParameters(std::string filename); ///< Save image generation parameters to .ffp file. void LoadParameters(std::string filename, bool fix_seed=false); ///< Load image generation parameters from .ffp file. template< class ParameterType > ParameterType ReadVal(boost::property_tree::ptree::value_type const& v, std::string tag, ParameterType defaultValue, bool essential=false); std::string m_MissingTags; }; } #endif diff --git a/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkStreamlineTractographyParameters.cpp b/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkStreamlineTractographyParameters.cpp new file mode 100644 index 0000000000..9f704669d5 --- /dev/null +++ b/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkStreamlineTractographyParameters.cpp @@ -0,0 +1,367 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +mitk::StreamlineTractographyParameters::StreamlineTractographyParameters() +{ + AutoAdjust(); +} + +mitk::StreamlineTractographyParameters::~StreamlineTractographyParameters() +{ + +} + +void mitk::StreamlineTractographyParameters::SaveParameters(std::string filename) +{ + if(filename.empty()) + return; + if(".stp"!=filename.substr(filename.size()-4, 4)) + filename += ".stp"; + + const std::string& locale = "C"; + const std::string& currLocale = setlocale( LC_ALL, nullptr ); + + if ( locale.compare(currLocale)!=0 ) + { + try + { + setlocale(LC_ALL, locale.c_str()); + } + catch(...) + { + MITK_INFO << "Could not set locale " << locale; + } + } + + boost::property_tree::ptree parameters; + + parameters.put("seeding.seeds_per_voxel", m_SeedsPerVoxel); + parameters.put("seeding.trials_per_seed", m_TrialsPerSeed); + parameters.put("seeding.max_num_fibers", m_MaxNumFibers); + parameters.put("seeding.interactive_radius_mm", m_InteractiveRadiusMm); + parameters.put("seeding.num_interactive_seeds", m_NumInteractiveSeeds); + parameters.put("seeding.enable_interactive", m_EnableInteractive); + + parameters.put("roi_constraints.ep_constraints", m_EpConstraints); + + parameters.put("tractography.mode", m_Mode); + parameters.put("tractography.sharpen_odfs", m_SharpenOdfs); + parameters.put("tractography.cutoff", m_Cutoff); + parameters.put("tractography.odf_cutoff", m_OdfCutoff); + parameters.put("tractography.step_size_vox", m_StepSizeVox); + parameters.put("tractography.min_tract_length_mm", m_MinTractLengthMm); + parameters.put("tractography.max_tract_length_mm", m_MaxTractLengthMm); + parameters.put("tractography.angluar_threshold_deg", m_AngularThresholdDeg); + parameters.put("tractography.loop_check_deg", m_LoopCheckDeg); + parameters.put("tractography.f", m_F); + parameters.put("tractography.g", m_G); + parameters.put("tractography.fix_seed", m_FixRandomSeed); + parameters.put("tractography.peak_jitter", m_PeakJitter); + + parameters.put("prior.weight", m_Weight); + parameters.put("prior.restrict_to_prior", m_RestrictToPrior); + parameters.put("prior.new_directions", m_NewDirectionsFromPrior); + parameters.put("prior.flip_x", m_PriorFlipX); + parameters.put("prior.flip_y", m_PriorFlipY); + parameters.put("prior.flip_z", m_PriorFlipZ); + + parameters.put("nsampling.num_samples", m_NumSamples); + parameters.put("nsampling.sampling_distance_vox", m_SamplingDistanceVox); + parameters.put("nsampling.only_frontal", m_OnlyForwardSamples); + parameters.put("nsampling.stop_votes", m_StopVotes); + + parameters.put("data_handling.flip_x", m_FlipX); + parameters.put("data_handling.flip_y", m_FlipY); + parameters.put("data_handling.flip_z", m_FlipZ); + parameters.put("data_handling.interpolate_tracto_data", m_InterpolateTractographyData); + parameters.put("data_handling.interpolate_roi_images", m_InterpolateRoiImages); + + parameters.put("output.compress", m_CompressFibers); + parameters.put("output.compression", m_Compression); + parameters.put("output.prob_map", m_OutputProbMap); + + boost::property_tree::json_parser::write_json(filename, parameters, std::locale(), true); + +// try{ +// itk::ImageFileWriter::Pointer writer = itk::ImageFileWriter::New(); +// writer->SetFileName(filename+"_FMAP.nii.gz"); +// writer->SetInput(m_SignalGen.m_FrequencyMap); +// writer->Update(); +// } +// catch(...) +// { +// MITK_INFO << "No frequency map saved."; +// } + + setlocale(LC_ALL, currLocale.c_str()); +} + + + +template< class ParameterType > +ParameterType mitk::StreamlineTractographyParameters::ReadVal(boost::property_tree::ptree::value_type const& v, std::string tag, ParameterType defaultValue, bool essential) +{ + try + { + return v.second.get(tag); + } + catch (...) + { + if (essential) + { + mitkThrow() << "Parameter file corrupted. Essential tag is missing: '" << tag << "'"; + } + MITK_INFO << "Tag '" << tag << "' not found. Using default value '" << defaultValue << "'."; + return defaultValue; + } +} + +void mitk::StreamlineTractographyParameters::LoadParameters(std::string filename) +{ + if(filename.empty()) { return; } + + const std::string& locale = "C"; + const std::string& currLocale = setlocale( LC_ALL, nullptr ); + + if ( locale.compare(currLocale)!=0 ) + { + try + { + setlocale(LC_ALL, locale.c_str()); + } + catch(...) + { + MITK_INFO << "Could not set locale " << locale; + } + } + + + boost::property_tree::ptree parameterTree; + boost::property_tree::json_parser::read_json( filename, parameterTree ); + + BOOST_FOREACH( boost::property_tree::ptree::value_type const& v1, parameterTree ) + { + if( v1.first == "seeding" ) + { + m_SeedsPerVoxel = ReadVal(v1,"seeds_per_voxel", m_SeedsPerVoxel); + m_TrialsPerSeed = ReadVal(v1,"trials_per_seed", m_TrialsPerSeed); + m_MaxNumFibers = ReadVal(v1,"max_num_fibers", m_MaxNumFibers); + m_InteractiveRadiusMm = ReadVal(v1,"interactive_radius_mm", m_InteractiveRadiusMm); + m_NumInteractiveSeeds = ReadVal(v1,"num_interactive_seeds", m_NumInteractiveSeeds); + m_EnableInteractive = ReadVal(v1,"enable_interactive", m_EnableInteractive); + } + else if( v1.first == "roi_constraints" ) + { + switch( ReadVal(v1,"ep_constraints", 0) ) + { + default: + m_EpConstraints = EndpointConstraints::NONE; + break; + case 1: + m_EpConstraints = EndpointConstraints::EPS_IN_TARGET; + break; + case 2: + m_EpConstraints = EndpointConstraints::EPS_IN_TARGET_LABELDIFF; + break; + case 3: + m_EpConstraints = EndpointConstraints::EPS_IN_SEED_AND_TARGET; + break; + case 4: + m_EpConstraints = EndpointConstraints::MIN_ONE_EP_IN_TARGET; + break; + case 5: + m_EpConstraints = EndpointConstraints::ONE_EP_IN_TARGET; + break; + case 6: + m_EpConstraints = EndpointConstraints::NO_EP_IN_TARGET; + break; + } + } + else if( v1.first == "tractography" ) + { + if(ReadVal(v1,"mode", 0) == 0) + m_Mode = MODE::DETERMINISTIC; + else + m_Mode = MODE::PROBABILISTIC; + + m_SharpenOdfs = ReadVal(v1,"sharpen_odfs", m_SharpenOdfs); + m_Cutoff = ReadVal(v1,"cutoff", m_Cutoff); + m_OdfCutoff = ReadVal(v1,"odf_cutoff", m_OdfCutoff); + SetStepSizeVox(ReadVal(v1,"step_size_vox", m_StepSizeVox)); + m_MinTractLengthMm = ReadVal(v1,"min_tract_length_mm", m_MinTractLengthMm); + m_MaxTractLengthMm = ReadVal(v1,"max_tract_length_mm", m_MaxTractLengthMm); + SetAngularThresholdDeg(ReadVal(v1,"angluar_threshold_deg", m_AngularThresholdDeg)); + SetLoopCheckDeg(ReadVal(v1,"loop_check_deg", m_LoopCheckDeg)); + m_F = ReadVal(v1,"f", m_F); + m_G = ReadVal(v1,"g", m_G); + m_FixRandomSeed = ReadVal(v1,"fix_seed", m_FixRandomSeed); + m_PeakJitter = ReadVal(v1,"peak_jitter", m_PeakJitter); + } + else if( v1.first == "prior" ) + { + m_Weight = ReadVal(v1,"weight", m_Weight); + m_RestrictToPrior = ReadVal(v1,"restrict_to_prior", m_RestrictToPrior); + m_NewDirectionsFromPrior = ReadVal(v1,"new_directions", m_NewDirectionsFromPrior); + m_PriorFlipX = ReadVal(v1,"flip_x", m_PriorFlipX); + m_PriorFlipY = ReadVal(v1,"flip_y", m_PriorFlipY); + m_PriorFlipZ = ReadVal(v1,"flip_z", m_PriorFlipZ); + } + else if( v1.first == "nsampling" ) + { + m_NumSamples = ReadVal(v1,"num_samples", m_NumSamples); + m_SamplingDistanceVox = ReadVal(v1,"sampling_distance_vox", m_SamplingDistanceVox); + m_OnlyForwardSamples = ReadVal(v1,"only_frontal", m_OnlyForwardSamples); + m_StopVotes = ReadVal(v1,"stop_votes", m_StopVotes); + } + else if( v1.first == "data_handling" ) + { + m_FlipX = ReadVal(v1,"flip_x", m_FlipX); + m_FlipY = ReadVal(v1,"flip_y", m_FlipY); + m_FlipZ = ReadVal(v1,"flip_z", m_FlipZ); + m_InterpolateTractographyData = ReadVal(v1,"interpolate_tracto_data", m_InterpolateTractographyData); + m_InterpolateRoiImages = ReadVal(v1,"interpolate_roi_images", m_InterpolateRoiImages); + } + else if( v1.first == "output" ) + { + m_CompressFibers = ReadVal(v1,"compress", m_CompressFibers); + m_Compression = ReadVal(v1,"compression", m_Compression); + m_OutputProbMap = ReadVal(v1,"prob_map", m_OutputProbMap); + } + } + +// try +// { +// itk::ImageFileReader::Pointer reader = itk::ImageFileReader::New(); +// reader->SetFileName(filename+"_FMAP.nrrd"); +// if ( itksys::SystemTools::FileExists(filename+"_FMAP.nii.gz") ) +// reader->SetFileName(filename+"_FMAP.nii.gz"); +// else if ( itksys::SystemTools::FileExists(filename+"_FMAP.nii") ) +// reader->SetFileName(filename+"_FMAP.nii"); +// else +// reader->SetFileName(filename+"_FMAP.nrrd"); +// reader->Update(); +// m_SignalGen.m_FrequencyMap = reader->GetOutput(); +// MITK_INFO << "Frequency map loaded."; +// } +// catch(...) +// { +// MITK_INFO << "No frequency map found."; +// } + + setlocale(LC_ALL, currLocale.c_str()); +} + +float mitk::StreamlineTractographyParameters::GetSamplingDistanceMm() const +{ + return m_SamplingDistanceMm; +} + +void mitk::StreamlineTractographyParameters::SetSamplingDistanceVox(float sampling_distance_vox) +{ + m_SamplingDistanceVox = sampling_distance_vox; + AutoAdjust(); +} + +void mitk::StreamlineTractographyParameters::AutoAdjust() +{ + if (m_StepSizeVox(mitk::eps)) + m_StepSizeVox = 0.5; + m_StepSizeMm = m_StepSizeVox*m_MinVoxelSizeMm; + + if (m_AngularThresholdDeg<0) + { + if (m_StepSizeMm/m_MinVoxelSizeMm<=0.966f) // minimum 15° for automatic estimation + m_AngularThresholdDot = static_cast(std::cos( 0.5 * itk::Math::pi * static_cast(m_StepSizeMm/m_MinVoxelSizeMm) )); + else + m_AngularThresholdDot = static_cast(std::cos( 0.5 * itk::Math::pi * 0.966 )); + + m_AngularThresholdDeg = std::acos(m_AngularThresholdDot)*180.0/itk::Math::pi; + } + else + m_AngularThresholdDot = static_cast(std::cos( static_cast(m_AngularThresholdDeg)*itk::Math::pi/180.0 )); + + if (m_SamplingDistanceVox(mitk::eps)) + m_SamplingDistanceVox = m_MinVoxelSizeMm*0.25f; + m_SamplingDistanceMm = m_SamplingDistanceVox*m_MinVoxelSizeMm; +} + +float mitk::StreamlineTractographyParameters::GetStepSizeVox() const +{ + return m_StepSizeVox; +} + +float mitk::StreamlineTractographyParameters::GetAngularThresholdDeg() const +{ + return m_AngularThresholdDeg; +} + +float mitk::StreamlineTractographyParameters::GetSamplingDistanceVox() const +{ + return m_SamplingDistanceVox; +} + +float mitk::StreamlineTractographyParameters::GetMinVoxelSizeMm() const +{ + return m_MinVoxelSizeMm; +} + +float mitk::StreamlineTractographyParameters::GetStepSizeMm() const +{ + return m_StepSizeMm; +} + +void mitk::StreamlineTractographyParameters::SetMinVoxelSizeMm(float min_voxel_size_mm) +{ + m_MinVoxelSizeMm = min_voxel_size_mm; + AutoAdjust(); +} + +float mitk::StreamlineTractographyParameters::GetAngularThresholdDot() const +{ + return m_AngularThresholdDot; +} + +void mitk::StreamlineTractographyParameters::SetStepSizeVox(float step_size_vox) +{ + m_StepSizeVox = step_size_vox; + AutoAdjust(); +} + +float mitk::StreamlineTractographyParameters::GetLoopCheckDeg() const +{ + return m_LoopCheckDeg; +} + +void mitk::StreamlineTractographyParameters::SetLoopCheckDeg(float loop_check_deg) +{ + m_LoopCheckDeg = loop_check_deg; +} + +void mitk::StreamlineTractographyParameters::SetAngularThresholdDeg(float angular_threshold_deg) +{ + m_AngularThresholdDeg = angular_threshold_deg; + AutoAdjust(); +} diff --git a/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkStreamlineTractographyParameters.h b/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkStreamlineTractographyParameters.h new file mode 100644 index 0000000000..9a0a818f77 --- /dev/null +++ b/Modules/DiffusionImaging/FiberTracking/IODataStructures/mitkStreamlineTractographyParameters.h @@ -0,0 +1,163 @@ +#pragma once + +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include +#include +#include + +namespace mitk +{ + /** + * \brief Datastructure to manage streamline tractography parameters. + * + */ + class MITKFIBERTRACKING_EXPORT StreamlineTractographyParameters + { + + public: + + enum EndpointConstraints { + NONE, ///< No constraints on endpoint locations + EPS_IN_TARGET, ///< Both EPs are required to be located in the target image + EPS_IN_TARGET_LABELDIFF, ///< Both EPs are required to be located in the target image and the image values at the respective position needs to be distinct + EPS_IN_SEED_AND_TARGET, ///< One EP is required to be located in the seed image and one in the target image + MIN_ONE_EP_IN_TARGET, ///< At least one EP is required to be located in the target image + ONE_EP_IN_TARGET, ///< Exactly one EP is required to be located in the target image + NO_EP_IN_TARGET ///< No EP is allowed to be located in the target image + }; + + enum MODE { + DETERMINISTIC, + PROBABILISTIC + }; + + typedef itk::Image ItkFloatImgType; + typedef itk::Image ItkDoubleImgType; + typedef itk::Image ItkUcharImgType; + + StreamlineTractographyParameters(); + StreamlineTractographyParameters(const StreamlineTractographyParameters ¶ms) = default; + ~StreamlineTractographyParameters(); + + void SaveParameters(std::string filename); ///< Save image generation parameters to .stp file. + void LoadParameters(std::string filename); ///< Load image generation parameters from .stp file. + + template< class ParameterType > + ParameterType ReadVal(boost::property_tree::ptree::value_type const& v, std::string tag, ParameterType defaultValue, bool essential=false); + + // seeding + unsigned int m_SeedsPerVoxel = 1; + unsigned int m_TrialsPerSeed = 10; + int m_MaxNumFibers = -1; + // - seed image + + // interactive + float m_InteractiveRadiusMm = 2; + unsigned int m_NumInteractiveSeeds = 50; + bool m_EnableInteractive = false; + + // ROI constraints + EndpointConstraints m_EpConstraints; + // - mask image + // - stop image + // - exclusion image + // - target image + + // tractography + MODE m_Mode; + bool m_SharpenOdfs = false; + float m_Cutoff = 0.1; + // - fa/gfa image + float m_OdfCutoff = 0.00025; + float m_MinTractLengthMm = 20; + float m_MaxTractLengthMm = 400; + float m_F = 1; + float m_G = 0; + bool m_FixRandomSeed = false; + unsigned int m_NumPreviousDirections = 1; + float m_PeakJitter = 0.01; // actual jitter is drawn from a normal distribution with m_PeakJitter*fabs(direction_value) as standard deviation + + // prior + // - peak image + float m_Weight = 0.5; + bool m_RestrictToPrior = true; + bool m_NewDirectionsFromPrior = true; + bool m_PriorFlipX = false; + bool m_PriorFlipY = false; + bool m_PriorFlipZ = false; + + // neighborhood sampling + unsigned int m_NumSamples = 0; + bool m_OnlyForwardSamples = false; + bool m_StopVotes = false; + bool m_AvoidStop = true; + bool m_RandomSampling = false; + float m_DeflectionMod = 1.0; + + // data handling + bool m_FlipX = false; + bool m_FlipY = false; + bool m_FlipZ = false; + bool m_InterpolateTractographyData = true; + bool m_InterpolateRoiImages; + bool m_ApplyDirectionMatrix = false; + + // output and postprocessing + bool m_CompressFibers = true; + float m_Compression = 0.1; + bool m_OutputProbMap = false; + + float GetAngularThresholdDot() const; + float GetAngularThresholdDeg() const; + void SetAngularThresholdDeg(float angular_threshold_deg); + + float GetLoopCheckDeg() const; + void SetLoopCheckDeg(float loop_check_deg); + + float GetStepSizeMm() const; + float GetStepSizeVox() const; + void SetStepSizeVox(float step_size_vox); + + float GetSamplingDistanceMm() const; + float GetSamplingDistanceVox() const; + void SetSamplingDistanceVox(float sampling_distance_vox); + + void SetMinVoxelSizeMm(float min_voxel_size_mm); + float GetMinVoxelSizeMm() const; + + private: + + void AutoAdjust(); + + float m_SamplingDistanceVox = -1; + float m_SamplingDistanceMm; + + float m_AngularThresholdDeg = -1; + float m_AngularThresholdDot; + + float m_LoopCheckDeg = -1; + + float m_StepSizeVox = -1; + float m_StepSizeMm; + + float m_MinVoxelSizeMm = 1.0; + }; +} + diff --git a/Modules/DiffusionImaging/FiberTracking/Testing/mitkMachineLearningTrackingTest.cpp b/Modules/DiffusionImaging/FiberTracking/Testing/mitkMachineLearningTrackingTest.cpp index 2e94c0b96b..a2d3d72928 100644 --- a/Modules/DiffusionImaging/FiberTracking/Testing/mitkMachineLearningTrackingTest.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Testing/mitkMachineLearningTrackingTest.cpp @@ -1,109 +1,110 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include #include #include #include #include #include #include #include #include #include +#include #include "mitkTestFixture.h" class mitkMachineLearningTrackingTestSuite : public mitk::TestFixture { - CPPUNIT_TEST_SUITE(mitkMachineLearningTrackingTestSuite); - MITK_TEST(Track1); - CPPUNIT_TEST_SUITE_END(); + CPPUNIT_TEST_SUITE(mitkMachineLearningTrackingTestSuite); + MITK_TEST(Track1); + CPPUNIT_TEST_SUITE_END(); - typedef itk::Image ItkFloatImgType; + typedef itk::Image ItkFloatImgType; private: - /** Members used inside the different (sub-)tests. All members are initialized via setUp().*/ - mitk::FiberBundle::Pointer ref; - mitk::TrackingHandlerRandomForest<6, 100>* tfh; - mitk::Image::Pointer dwi; - ItkFloatImgType::Pointer seed; + /** Members used inside the different (sub-)tests. All members are initialized via setUp().*/ + mitk::FiberBundle::Pointer ref; + mitk::TrackingHandlerRandomForest<6, 100>* tfh; + mitk::Image::Pointer dwi; + ItkFloatImgType::Pointer seed; public: - void setUp() override - { - ref = nullptr; - tfh = new mitk::TrackingHandlerRandomForest<6,100>(); - - std::vector fibInfile = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/MachineLearningTracking/ReferenceTracts.fib")); - mitk::BaseData::Pointer baseData = fibInfile.at(0); - ref = dynamic_cast(baseData.GetPointer()); - - dwi = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/MachineLearningTracking/DiffusionImage.dwi")); - - - mitk::Image::Pointer img = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/MachineLearningTracking/seed.nrrd")); - seed = ItkFloatImgType::New(); - mitk::CastToItkImage(img, seed); - - mitk::TractographyForest::Pointer forest = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/MachineLearningTracking/forest.rf")); - - tfh->SetForest(forest); - tfh->AddDwi(dwi); - } - - void tearDown() override - { - delete tfh; - ref = nullptr; - } - - void Track1() - { - omp_set_num_threads(1); - typedef itk::StreamlineTrackingFilter TrackerType; - TrackerType::Pointer tracker = TrackerType::New(); - tracker->SetDemoMode(false); - tracker->SetInterpolateMasks(false); - tracker->SetSeedImage(seed); - tracker->SetSeedsPerVoxel(1); - tracker->SetStepSize(-1); - tracker->SetAngularThreshold(45); - tracker->SetMinTractLength(20); - tracker->SetMaxTractLength(400); - tracker->SetTrackingHandler(tfh); - tracker->SetAvoidStop(true); - tracker->SetSamplingDistance(0.5); - tracker->SetRandomSampling(false); - tracker->Update(); - vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); - mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); - - //MITK_INFO << mitk::IOUtil::GetTempPath() << "ReferenceTracts.fib"; - if (!ref->Equals(outFib)) - mitk::IOUtil::Save(outFib, mitk::IOUtil::GetTempPath()+"ML_Track1.fib"); - - CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(outFib)); - } + void setUp() override + { + ref = nullptr; + tfh = new mitk::TrackingHandlerRandomForest<6,100>(); + + std::vector fibInfile = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/MachineLearningTracking/ReferenceTracts.fib")); + mitk::BaseData::Pointer baseData = fibInfile.at(0); + ref = dynamic_cast(baseData.GetPointer()); + + dwi = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/MachineLearningTracking/DiffusionImage.dwi")); + + + mitk::Image::Pointer img = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/MachineLearningTracking/seed.nrrd")); + seed = ItkFloatImgType::New(); + mitk::CastToItkImage(img, seed); + + mitk::TractographyForest::Pointer forest = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/MachineLearningTracking/forest.rf")); + + tfh->SetForest(forest); + tfh->AddDwi(dwi); + } + + void tearDown() override + { + delete tfh; + ref = nullptr; + } + + void Track1() + { + std::shared_ptr params = std::make_shared(); + params->m_RandomSampling = false; + params->SetSamplingDistanceVox(0.5); + params->m_AvoidStop = true; + params->SetAngularThresholdDeg(45); + params->SetStepSizeVox(-1); + params->m_SeedsPerVoxel = 1; + params->m_InterpolateRoiImages = false; + omp_set_num_threads(1); + typedef itk::StreamlineTrackingFilter TrackerType; + TrackerType::Pointer tracker = TrackerType::New(); + tracker->SetDemoMode(false); + tracker->SetSeedImage(seed); + tracker->SetTrackingHandler(tfh); + tracker->SetParameters(params); + tracker->Update(); + vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); + mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); + + //MITK_INFO << mitk::IOUtil::GetTempPath() << "ReferenceTracts.fib"; + if (!ref->Equals(outFib)) + mitk::IOUtil::Save(outFib, mitk::IOUtil::GetTempPath()+"ML_Track1.fib"); + + CPPUNIT_ASSERT_MESSAGE("Should be equal", ref->Equals(outFib)); + } }; MITK_TEST_SUITE_REGISTRATION(mitkMachineLearningTracking) diff --git a/Modules/DiffusionImaging/FiberTracking/Testing/mitkStreamlineTractographyTest.cpp b/Modules/DiffusionImaging/FiberTracking/Testing/mitkStreamlineTractographyTest.cpp index 508a0df61d..edb22aadd7 100755 --- a/Modules/DiffusionImaging/FiberTracking/Testing/mitkStreamlineTractographyTest.cpp +++ b/Modules/DiffusionImaging/FiberTracking/Testing/mitkStreamlineTractographyTest.cpp @@ -1,426 +1,438 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include class mitkStreamlineTractographyTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkStreamlineTractographyTestSuite); MITK_TEST(Test_Peak1); MITK_TEST(Test_Peak2); MITK_TEST(Test_Tensor1); MITK_TEST(Test_Tensor2); MITK_TEST(Test_Tensor3); MITK_TEST(Test_Odf1); MITK_TEST(Test_Odf2); MITK_TEST(Test_Odf3); MITK_TEST(Test_Odf4); MITK_TEST(Test_Odf5); MITK_TEST(Test_Odf6); CPPUNIT_TEST_SUITE_END(); typedef itk::VectorImage< short, 3> ItkDwiType; private: public: /** Members used inside the different (sub-)tests. All members are initialized via setUp().*/ typedef itk::Image ItkFloatImgType; mitk::TrackingHandlerOdf::ItkOdfImageType::Pointer itk_odf_image; mitk::TrackingHandlerTensor::ItkTensorImageType::ConstPointer itk_tensor_image; mitk::TrackingHandlerPeaks::PeakImgType::Pointer itk_peak_image; ItkFloatImgType::Pointer itk_seed_image; ItkFloatImgType::Pointer itk_mask_image; ItkFloatImgType::Pointer itk_gfa_image; float gfa_threshold; float odf_threshold; float peak_threshold; + std::shared_ptr params; itk::StreamlineTrackingFilter::Pointer tracker; void setUp() override { omp_set_num_threads(1); gfa_threshold = 0.2f; odf_threshold = 0.1f; peak_threshold = 0.1f; mitk::Image::Pointer odf_image = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/qball_image.qbi")); mitk::Image::Pointer tensor_image = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/tensor_image.dti")); mitk::Image::Pointer peak_image = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/qball_peak_image.nii.gz")); mitk::Image::Pointer seed_image = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/seed_image.nii.gz")); mitk::Image::Pointer mask_image = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/mask_image.nii.gz")); mitk::Image::Pointer gfa_image = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/gfa_image.nii.gz")); + params = std::make_shared(); + params->m_FixRandomSeed = true; + params->m_InterpolateRoiImages = false; + { typedef mitk::ImageToItk< mitk::TrackingHandlerPeaks::PeakImgType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(peak_image); caster->Update(); itk_peak_image = caster->GetOutput(); } { typedef mitk::ImageToItk< mitk::TrackingHandlerTensor::ItkTensorImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(tensor_image); caster->Update(); itk_tensor_image = caster->GetOutput(); } { typedef mitk::ImageToItk< mitk::TrackingHandlerOdf::ItkOdfImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(odf_image); caster->Update(); itk_odf_image = caster->GetOutput(); } itk_gfa_image = ItkFloatImgType::New(); mitk::CastToItkImage(gfa_image, itk_gfa_image); itk_seed_image = ItkFloatImgType::New(); mitk::CastToItkImage(seed_image, itk_seed_image); itk_mask_image = ItkFloatImgType::New(); mitk::CastToItkImage(mask_image, itk_mask_image); } mitk::FiberBundle::Pointer LoadReferenceFib(std::string filename) { mitk::FiberBundle::Pointer fib = nullptr; if (itksys::SystemTools::FileExists(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/ReferenceFibs/" + filename))) { mitk::BaseData::Pointer baseData = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/ReferenceFibs/" + filename)).at(0); fib = dynamic_cast(baseData.GetPointer()); } return fib; } mitk::Image::Pointer LoadReferenceImage(std::string filename) { mitk::Image::Pointer img = nullptr; if (itksys::SystemTools::FileExists(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/ReferenceFibs/" + filename))) { img = mitk::IOUtil::Load(GetTestDataFilePath("DiffusionImaging/StreamlineTractography/ReferenceFibs/" + filename)); } return img; } void SetupTracker(mitk::TrackingDataHandler* handler) { + tracker = itk::StreamlineTrackingFilter::New(); - tracker->SetRandom(false); - tracker->SetInterpolateMasks(false); - tracker->SetNumberOfSamples(0); - tracker->SetAngularThreshold(-1); + +// tracker->SetInterpolateMasks(false); +// tracker->SetNumberOfSamples(0); +// tracker->SetAngularThreshold(-1); tracker->SetMaskImage(itk_mask_image); tracker->SetSeedImage(itk_seed_image); tracker->SetStoppingRegions(nullptr); - tracker->SetSeedsPerVoxel(1); - tracker->SetStepSize(0.5); - tracker->SetSamplingDistance(0.25); - tracker->SetUseStopVotes(true); - tracker->SetOnlyForwardSamples(true); - tracker->SetMinTractLength(20); - tracker->SetMaxNumTracts(-1); +// tracker->SetSeedsPerVoxel(1); +// tracker->SetStepSize(0.5); +// tracker->SetSamplingDistance(0.25); +// tracker->SetUseStopVotes(true); +// tracker->SetOnlyForwardSamples(true); +// tracker->SetMinTractLength(20); +// tracker->SetMaxNumTracts(-1); tracker->SetTrackingHandler(handler); - tracker->SetUseOutputProbabilityMap(false); +// tracker->SetUseOutputProbabilityMap(false); + tracker->SetParameters(params); } void tearDown() override { } void CheckFibResult(std::string ref_file, mitk::FiberBundle::Pointer test_fib) { mitk::FiberBundle::Pointer ref = LoadReferenceFib(ref_file); if (ref.IsNull()) { mitk::IOUtil::Save(test_fib, mitk::IOUtil::GetTempPath()+ref_file); CPPUNIT_FAIL("Reference file not found. Saving test file to " + mitk::IOUtil::GetTempPath() + ref_file); } else { bool is_equal = ref->Equals(test_fib); if (!is_equal) { mitk::IOUtil::Save(test_fib, mitk::IOUtil::GetTempPath()+ref_file); CPPUNIT_FAIL("Tractograms are not equal! Saving test file to " + mitk::IOUtil::GetTempPath() + ref_file); } } } void CheckImageResult(std::string ref_file, mitk::Image::Pointer test_img) { mitk::Image::Pointer ref = LoadReferenceImage(ref_file); if (ref.IsNull()) { mitk::IOUtil::Save(test_img, mitk::IOUtil::GetTempPath()+ref_file); CPPUNIT_FAIL("Reference file not found. Saving test file to " + mitk::IOUtil::GetTempPath() + ref_file); } else { MITK_ASSERT_EQUAL(test_img, ref, "Images should be equal"); } } void Test_Peak1() { mitk::TrackingHandlerPeaks* handler = new mitk::TrackingHandlerPeaks(); handler->SetPeakImage(itk_peak_image); - handler->SetPeakThreshold(peak_threshold); - + params->m_Cutoff = peak_threshold; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Peak1.fib", outFib); delete handler; } void Test_Peak2() { mitk::TrackingHandlerPeaks* handler = new mitk::TrackingHandlerPeaks(); handler->SetPeakImage(itk_peak_image); - handler->SetPeakThreshold(peak_threshold); - handler->SetInterpolate(false); + params->m_Cutoff = peak_threshold; + params->m_InterpolateTractographyData = false; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Peak2.fib", outFib); delete handler; } void Test_Tensor1() { mitk::TrackingHandlerTensor* handler = new mitk::TrackingHandlerTensor(); handler->SetTensorImage(itk_tensor_image); - handler->SetFaThreshold(gfa_threshold); - + params->m_Cutoff = gfa_threshold; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Tensor1.fib", outFib); delete handler; } void Test_Tensor2() { mitk::TrackingHandlerTensor* handler = new mitk::TrackingHandlerTensor(); handler->SetTensorImage(itk_tensor_image); - handler->SetFaThreshold(gfa_threshold); - handler->SetInterpolate(false); + params->m_Cutoff = gfa_threshold; + params->m_InterpolateTractographyData = false; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Tensor2.fib", outFib); delete handler; } void Test_Tensor3() { mitk::TrackingHandlerTensor* handler = new mitk::TrackingHandlerTensor(); handler->SetTensorImage(itk_tensor_image); - handler->SetFaThreshold(gfa_threshold); - handler->SetInterpolate(false); - handler->SetF(0); - handler->SetG(1); + params->m_Cutoff = gfa_threshold; + params->m_InterpolateTractographyData = false; + params->m_F = 0; + params->m_G = 1; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Tensor3.fib", outFib); delete handler; } void Test_Odf1() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); - handler->SetGfaThreshold(gfa_threshold); - handler->SetOdfThreshold(0); - handler->SetSharpenOdfs(true); + + params->m_Cutoff = gfa_threshold; + params->m_OdfCutoff = 0; + params->m_SharpenOdfs = true; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Odf1.fib", outFib); delete handler; } void Test_Odf2() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); - handler->SetGfaThreshold(gfa_threshold); - handler->SetOdfThreshold(0); - handler->SetSharpenOdfs(false); + + params->m_Cutoff = gfa_threshold; + params->m_OdfCutoff = 0; + params->m_SharpenOdfs = false; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Odf2.fib", outFib); delete handler; } void Test_Odf3() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); - handler->SetGfaThreshold(gfa_threshold); - handler->SetOdfThreshold(0); - handler->SetSharpenOdfs(true); - handler->SetInterpolate(false); + + params->m_Cutoff = gfa_threshold; + params->m_OdfCutoff = 0; + params->m_SharpenOdfs = true; + params->m_InterpolateTractographyData = false; SetupTracker(handler); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Odf3.fib", outFib); delete handler; } void Test_Odf4() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); - handler->SetGfaThreshold(gfa_threshold); - handler->SetOdfThreshold(0); - handler->SetSharpenOdfs(true); + + params->m_Cutoff = gfa_threshold; + params->m_OdfCutoff = 0; + params->m_SharpenOdfs = true; + params->m_SeedsPerVoxel = 3; SetupTracker(handler); - tracker->SetSeedsPerVoxel(3); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Odf4.fib", outFib); delete handler; } void Test_Odf5() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); - handler->SetGfaThreshold(gfa_threshold); - handler->SetOdfThreshold(0); - handler->SetSharpenOdfs(true); - handler->SetMode(mitk::TrackingDataHandler::MODE::PROBABILISTIC); + + params->m_Cutoff = gfa_threshold; + params->m_OdfCutoff = 0; + params->m_SharpenOdfs = true; + params->m_SeedsPerVoxel = 3; + params->m_Mode = mitk::TrackingDataHandler::MODE::PROBABILISTIC; SetupTracker(handler); - tracker->SetSeedsPerVoxel(3); tracker->Update(); vtkSmartPointer< vtkPolyData > poly = tracker->GetFiberPolyData(); mitk::FiberBundle::Pointer outFib = mitk::FiberBundle::New(poly); CheckFibResult("Test_Odf5.fib", outFib); delete handler; } void Test_Odf6() { mitk::TrackingHandlerOdf* handler = new mitk::TrackingHandlerOdf(); handler->SetOdfImage(itk_odf_image); - handler->SetGfaThreshold(gfa_threshold); - handler->SetOdfThreshold(0); - handler->SetSharpenOdfs(true); - handler->SetMode(mitk::TrackingDataHandler::MODE::PROBABILISTIC); + + params->m_Cutoff = gfa_threshold; + params->m_OdfCutoff = 0; + params->m_SharpenOdfs = true; + params->m_SeedsPerVoxel = 10; + params->m_Mode = mitk::TrackingDataHandler::MODE::PROBABILISTIC; + params->m_OutputProbMap = true; SetupTracker(handler); - tracker->SetSeedsPerVoxel(10); - tracker->SetUseOutputProbabilityMap(true); tracker->Update(); itk::StreamlineTrackingFilter::ItkDoubleImgType::Pointer outImg = tracker->GetOutputProbabilityMap(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); mitk::IOUtil::Save(img, mitk::IOUtil::GetTempPath()+"Test_Odf6.nrrd"); CheckImageResult("Test_Odf6.nrrd", img); delete handler; } }; MITK_TEST_SUITE_REGISTRATION(mitkStreamlineTractography) diff --git a/Modules/DiffusionImaging/FiberTracking/files.cmake b/Modules/DiffusionImaging/FiberTracking/files.cmake index 19ac812b48..39210be9cf 100644 --- a/Modules/DiffusionImaging/FiberTracking/files.cmake +++ b/Modules/DiffusionImaging/FiberTracking/files.cmake @@ -1,109 +1,111 @@ set(CPP_FILES mitkFiberTrackingModuleActivator.cpp ## IO datastructures IODataStructures/FiberBundle/mitkFiberBundle.cpp IODataStructures/FiberBundle/mitkTrackvis.cpp IODataStructures/PlanarFigureComposite/mitkPlanarFigureComposite.cpp IODataStructures/mitkTractographyForest.cpp IODataStructures/mitkFiberfoxParameters.cpp + IODataStructures/mitkStreamlineTractographyParameters.cpp # Interactions # Tractography Algorithms/GibbsTracking/mitkParticleGrid.cpp Algorithms/GibbsTracking/mitkMetropolisHastingsSampler.cpp Algorithms/GibbsTracking/mitkEnergyComputer.cpp Algorithms/GibbsTracking/mitkGibbsEnergyComputer.cpp Algorithms/GibbsTracking/mitkFiberBuilder.cpp Algorithms/GibbsTracking/mitkSphereInterpolator.cpp Algorithms/itkStreamlineTrackingFilter.cpp Algorithms/TrackingHandlers/mitkTrackingDataHandler.cpp Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.cpp Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.cpp Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.cpp ) set(H_FILES # DataStructures -> FiberBundle IODataStructures/FiberBundle/mitkFiberBundle.h IODataStructures/FiberBundle/mitkTrackvis.h IODataStructures/mitkFiberfoxParameters.h IODataStructures/mitkTractographyForest.h + IODataStructures/mitkStreamlineTractographyParameters.h # Algorithms Algorithms/itkTractDensityImageFilter.h Algorithms/itkTractsToFiberEndingsImageFilter.h Algorithms/itkTractsToRgbaImageFilter.h Algorithms/itkTractsToVectorImageFilter.h Algorithms/itkEvaluateDirectionImagesFilter.h Algorithms/itkEvaluateTractogramDirectionsFilter.h Algorithms/itkFiberCurvatureFilter.h Algorithms/itkFitFibersToImageFilter.h Algorithms/itkTractClusteringFilter.h Algorithms/itkTractDistanceFilter.h Algorithms/itkFiberExtractionFilter.h Algorithms/itkTdiToVolumeFractionFilter.h # Tractography Algorithms/TrackingHandlers/mitkTrackingDataHandler.h Algorithms/TrackingHandlers/mitkTrackingHandlerRandomForest.h Algorithms/TrackingHandlers/mitkTrackingHandlerTensor.h Algorithms/TrackingHandlers/mitkTrackingHandlerPeaks.h Algorithms/TrackingHandlers/mitkTrackingHandlerOdf.h Algorithms/itkGibbsTrackingFilter.h Algorithms/itkStochasticTractographyFilter.h Algorithms/GibbsTracking/mitkParticle.h Algorithms/GibbsTracking/mitkParticleGrid.h Algorithms/GibbsTracking/mitkMetropolisHastingsSampler.h Algorithms/GibbsTracking/mitkSimpSamp.h Algorithms/GibbsTracking/mitkEnergyComputer.h Algorithms/GibbsTracking/mitkGibbsEnergyComputer.h Algorithms/GibbsTracking/mitkSphereInterpolator.h Algorithms/GibbsTracking/mitkFiberBuilder.h Algorithms/itkStreamlineTrackingFilter.h # Clustering Algorithms/ClusteringMetrics/mitkClusteringMetric.h Algorithms/ClusteringMetrics/mitkClusteringMetricEuclideanMean.h Algorithms/ClusteringMetrics/mitkClusteringMetricEuclideanMax.h Algorithms/ClusteringMetrics/mitkClusteringMetricEuclideanStd.h Algorithms/ClusteringMetrics/mitkClusteringMetricAnatomic.h Algorithms/ClusteringMetrics/mitkClusteringMetricScalarMap.h Algorithms/ClusteringMetrics/mitkClusteringMetricInnerAngles.h Algorithms/ClusteringMetrics/mitkClusteringMetricLength.h # Fiberfox Fiberfox/itkFibersFromPlanarFiguresFilter.h Fiberfox/itkTractsToDWIImageFilter.h Fiberfox/itkKspaceImageFilter.h Fiberfox/itkDftImageFilter.h Fiberfox/itkFieldmapGeneratorFilter.h Fiberfox/itkRandomPhantomFilter.h Fiberfox/SignalModels/mitkDiffusionSignalModel.h Fiberfox/SignalModels/mitkTensorModel.h Fiberfox/SignalModels/mitkBallModel.h Fiberfox/SignalModels/mitkDotModel.h Fiberfox/SignalModels/mitkAstroStickModel.h Fiberfox/SignalModels/mitkStickModel.h Fiberfox/SignalModels/mitkRawShModel.h Fiberfox/SignalModels/mitkDiffusionNoiseModel.h Fiberfox/SignalModels/mitkRicianNoiseModel.h Fiberfox/SignalModels/mitkChiSquareNoiseModel.h Fiberfox/Sequences/mitkAcquisitionType.h Fiberfox/Sequences/mitkSingleShotEpi.h Fiberfox/Sequences/mitkConventionalSpinEcho.h Fiberfox/Sequences/mitkFastSpinEcho.h ) set(RESOURCE_FILES # Binary directory resources FiberTrackingLUTBaryCoords.bin FiberTrackingLUTIndices.bin ) diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.cpp deleted file mode 100644 index 5a1b359889..0000000000 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.cpp +++ /dev/null @@ -1,334 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#include "QmitkImageStatisticsTableModel.h" - -#include "mitkProportionalTimeGeometry.h" -#include "mitkStatisticsToImageRelationRule.h" -#include "mitkImageStatisticsContainerManager.h" -#include "itkMutexLockHolder.h" - - -QmitkImageStatisticsTableModel::QmitkImageStatisticsTableModel(QObject *parent) : - QmitkAbstractDataStorageModel(parent) -{} - -QmitkImageStatisticsTableModel -::~QmitkImageStatisticsTableModel() -{ - // set data storage to nullptr so that the event listener gets removed - this->SetDataStorage(nullptr); -}; - -void QmitkImageStatisticsTableModel::DataStorageChanged() -{ - emit beginResetModel(); - UpdateByDataStorage(); - emit endResetModel(); -} - -void QmitkImageStatisticsTableModel::NodePredicateChanged() -{ - emit beginResetModel(); - UpdateByDataStorage(); - emit endResetModel(); -} - - -int QmitkImageStatisticsTableModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - { - return 0; - } - return m_StatisticNames.size(); -} - -int QmitkImageStatisticsTableModel::columnCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - - if (m_ViewMode == ViewMode::imageXStatistic) - { - return static_cast(m_TimeStepResolvedImageNodes.size()); - } - else - { - mitkThrow() << "View mode ImageXMask is not implemented yet"; - } -} - -std::pair QmitkImageStatisticsTableModel::GetRelevantStatsticsByIndex(const QModelIndex &index) const -{ - auto result = std::make_pair(nullptr, 0); - - if (m_ViewMode == ViewMode::imageXStatistic) - { - if (index.column() < static_cast(m_TimeStepResolvedImageNodes.size())) - { - auto selectedImage = m_TimeStepResolvedImageNodes.at(static_cast(index.column())); - - //get the right statistic - auto rule = mitk::StatisticsToImageRelationRule::New(); - for (mitk::ImageStatisticsContainer::ConstPointer container : m_Statistics) - { - if (rule->HasRelation(container, selectedImage.first->GetData()) >= mitk::PropertyRelationRuleBase::RelationType::Connected_Data) - { - return std::make_pair(container, selectedImage.second); - } - } - } - } - else - { - mitkThrow() << "View mode ImageXMask is not implemented yet"; - } - - return result; -} - -QVariant QmitkImageStatisticsTableModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - QVariant result; - if (m_ViewMode == ViewMode::imageXStatistic) { - - auto containerInfo = GetRelevantStatsticsByIndex(index); - - if (containerInfo.first.IsNotNull() && index.row() < rowCount(QModelIndex())) - { - if (Qt::DisplayRole == role) - { - if (containerInfo.first->TimeStepExists(containerInfo.second)) - { - auto statsObj = containerInfo.first->GetStatisticsForTimeStep(containerInfo.second); - auto statisticKey = m_StatisticNames.at(index.row()); - std::stringstream ss; - if (statsObj.HasStatistic(statisticKey)) - { - ss << statsObj.GetValueNonConverted(statisticKey); - } - else - { - ss << "N/A"; - } - result = QVariant(QString::fromStdString(ss.str())); - } - else - { - result = QVariant(QString::fromStdString("N/A")); - } - } - else if (Qt::UserRole == role) - { - result = QVariant(index.row()); - } - - } - } - - return result; -} - -QModelIndex QmitkImageStatisticsTableModel::index(int row, int column, const QModelIndex &parent) const -{ - bool hasIndex = this->hasIndex(row, column, parent); - if (hasIndex) - { - return this->createIndex(row, column); - } - - return QModelIndex(); -} - -QModelIndex QmitkImageStatisticsTableModel::parent(const QModelIndex &/*child*/) const -{ - return QModelIndex(); -} - - -Qt::ItemFlags QmitkImageStatisticsTableModel::flags(const QModelIndex &index) const -{ - Qt::ItemFlags flags = QAbstractItemModel::flags(index); - - return flags; -} - -QVariant QmitkImageStatisticsTableModel::headerData(int section, Qt::Orientation orientation, int role) const -{ - if ((Qt::DisplayRole == role) && (Qt::Horizontal == orientation)) - { - if (!m_TimeStepResolvedImageNodes.empty()) { - if (m_ViewMode == ViewMode::imageXStatistic) { - std::stringstream ss; - auto imageInfo = m_TimeStepResolvedImageNodes.at(section); - ss << imageInfo.first->GetName(); - if (imageInfo.first->GetData() && imageInfo.first->GetData()->GetTimeSteps() > 1) - { - ss << " #" << imageInfo.second; - } - if (!m_MaskNodes.empty() && m_MaskNodes.size() == m_ImageNodes.size()) { - auto maskInfo = m_TimeStepResolvedMaskNodes.at(section); - ss << " / " << maskInfo.first->GetName(); - } - return QVariant(ss.str().c_str()); - } - } - } - else if ((Qt::DisplayRole == role) && (Qt::Vertical == orientation)) { - if (m_ViewMode == ViewMode::imageXStatistic) { - return QVariant(m_StatisticNames.at(section).c_str()); - } - } - return QVariant(); -} - -void QmitkImageStatisticsTableModel::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(); -} - -void QmitkImageStatisticsTableModel::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(); -} - -void QmitkImageStatisticsTableModel::SetViewMode(ViewMode m) -{ - emit beginResetModel(); - m_ViewMode = m; - emit endResetModel(); -} - -void QmitkImageStatisticsTableModel::Clear() -{ - emit beginResetModel(); - m_Statistics.clear(); - m_ImageNodes.clear(); - m_TimeStepResolvedImageNodes.clear(); - m_MaskNodes.clear(); - m_StatisticNames.clear(); - emit endResetModel(); -} - -void QmitkImageStatisticsTableModel::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()); - if (stats.IsNotNull()) - { - newStatistics.emplace_back(stats); - } - } - else - { - for (const auto& mask : m_MaskNodes) - { - auto stats = mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), mask->GetData()); - if (stats.IsNotNull()) - { - newStatistics.emplace_back(stats); - } - } - } - } - if (!newStatistics.empty()) - { - emit dataAvailable(); - } - } - - { - itk::MutexLockHolder locked(m_Mutex); - m_Statistics = newStatistics; - } - - m_StatisticNames = mitk::GetAllStatisticNames(m_Statistics); -} - -void QmitkImageStatisticsTableModel::NodeRemoved(const mitk::DataNode *) -{ - emit beginResetModel(); - UpdateByDataStorage(); - emit endResetModel(); -} - -void QmitkImageStatisticsTableModel::NodeAdded(const mitk::DataNode *) -{ - emit beginResetModel(); - UpdateByDataStorage(); - emit endResetModel(); -} - -void QmitkImageStatisticsTableModel::NodeChanged(const mitk::DataNode *) -{ - emit beginResetModel(); - UpdateByDataStorage(); - emit endResetModel(); -} diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeItem.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeItem.cpp new file mode 100644 index 0000000000..7042b3210d --- /dev/null +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeItem.cpp @@ -0,0 +1,104 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkImageStatisticsTreeItem.h" + +QmitkImageStatisticsTreeItem::QmitkImageStatisticsTreeItem( + ImageStatisticsObject statisticsData, + StatisticNameVector statisticNames, + QVariant label, + QmitkImageStatisticsTreeItem *parent) + : m_statistics(statisticsData) , m_statisticNames(statisticNames), m_label(label), m_parentItem(parent) +{ +} + + QmitkImageStatisticsTreeItem::QmitkImageStatisticsTreeItem(StatisticNameVector statisticNames, + QVariant label, + QmitkImageStatisticsTreeItem *parentItem) + : QmitkImageStatisticsTreeItem(ImageStatisticsObject(), statisticNames, label, parentItem ) +{ +} + + QmitkImageStatisticsTreeItem::QmitkImageStatisticsTreeItem() : QmitkImageStatisticsTreeItem(StatisticNameVector(), QVariant(), nullptr ) {} + +QmitkImageStatisticsTreeItem::~QmitkImageStatisticsTreeItem() +{ + qDeleteAll(m_childItems); +} + +void QmitkImageStatisticsTreeItem::appendChild(QmitkImageStatisticsTreeItem *item) +{ + m_childItems.append(item); +} + +QmitkImageStatisticsTreeItem *QmitkImageStatisticsTreeItem::child(int row) +{ + return m_childItems.value(row); +} + +int QmitkImageStatisticsTreeItem::childCount() const +{ + return m_childItems.count(); +} + +int QmitkImageStatisticsTreeItem::columnCount() const +{ + return m_statisticNames.size() + 1; +} + +QVariant QmitkImageStatisticsTreeItem::data(int column) const +{ + QVariant result; + if (column > 0 && !m_statisticNames.empty()) + { + if (column - 1 < static_cast(m_statisticNames.size())) + { + auto statisticKey = m_statisticNames.at(column - 1); + std::stringstream ss; + if (m_statistics.HasStatistic(statisticKey)) + { + ss << m_statistics.GetValueNonConverted(statisticKey); + } + else + { + return QVariant(); + } + result = QVariant(QString::fromStdString(ss.str())); + } + else + { + return QVariant(); + } + } + else if (column == 0) + { + result = m_label; + } + return result; +} + +QmitkImageStatisticsTreeItem *QmitkImageStatisticsTreeItem::parentItem() +{ + return m_parentItem; +} + +int QmitkImageStatisticsTreeItem::row() const +{ + if (m_parentItem) + return m_parentItem->m_childItems.indexOf(const_cast(this)); + + return 0; +} diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeItem.h b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeItem.h new file mode 100644 index 0000000000..f28ca97cf3 --- /dev/null +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeItem.h @@ -0,0 +1,58 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QmitkImageStatisticsTreeItem_h +#define QmitkImageStatisticsTreeItem_h + +#include +#include + +#include "mitkImageStatisticsContainer.h" + +/*! +\class QmitkImageStatisticsTreeItem +An item that represents an entry (usually ImageStatisticsObject) for the QmitkImageStatisticsTreeModel +*/ +class QmitkImageStatisticsTreeItem +{ +public: + using ImageStatisticsObject = mitk::ImageStatisticsContainer::ImageStatisticsObject; + using StatisticNameVector = mitk::ImageStatisticsContainer::ImageStatisticsObject::StatisticNameVector; + QmitkImageStatisticsTreeItem(); + explicit QmitkImageStatisticsTreeItem(ImageStatisticsObject statisticsData, + StatisticNameVector statisticNames, QVariant label, QmitkImageStatisticsTreeItem *parentItem = nullptr); + explicit QmitkImageStatisticsTreeItem(StatisticNameVector statisticNames, + QVariant label, QmitkImageStatisticsTreeItem *parentItem = nullptr); + ~QmitkImageStatisticsTreeItem(); + + void appendChild(QmitkImageStatisticsTreeItem *child); + + QmitkImageStatisticsTreeItem *child(int row); + int childCount() const; + int columnCount() const; + QVariant data(int column) const; + int row() const; + QmitkImageStatisticsTreeItem *parentItem(); + +private: + ImageStatisticsObject m_statistics; + StatisticNameVector m_statisticNames; + QVariant m_label; + QmitkImageStatisticsTreeItem *m_parentItem = nullptr; + QList m_childItems; +}; + +#endif // QmitkImageStatisticsTreeItem_h diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp new file mode 100644 index 0000000000..2598435e20 --- /dev/null +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.cpp @@ -0,0 +1,382 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkImageStatisticsTreeModel.h" + +#include "QmitkImageStatisticsTreeItem.h" +#include "itkMutexLockHolder.h" +#include "mitkImageStatisticsContainerManager.h" +#include "mitkProportionalTimeGeometry.h" +#include "mitkStatisticsToImageRelationRule.h" +#include "mitkStatisticsToMaskRelationRule.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(); + + if (role != Qt::DisplayRole) + return QVariant(); + + QmitkImageStatisticsTreeItem *item = static_cast(index.internalPointer()); + + return item->data(index.column()); +} + +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 0; + + 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::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()); + + if (stats.IsNotNull()) + { + newStatistics.emplace_back(stats); + } + } + else + { + for (const auto &mask : m_MaskNodes) + { + auto stats = + mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), mask->GetData()); + if (stats.IsNotNull()) + { + newStatistics.emplace_back(stats); + } + } + } + } + if (!newStatistics.empty()) + { + emit dataAvailable(); + } + } + + { + itk::MutexLockHolder 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 (auto statistic : m_Statistics) + { + // get the connected image data node/mask data node + auto imageRule = mitk::StatisticsToImageRelationRule::New(); + auto imageOfStatisticsPredicate = imageRule->GetDestinationsDetector(statistic); + auto imageCandidates = this->GetDataStorage()->GetSubset(imageOfStatisticsPredicate); + + auto maskRule = mitk::StatisticsToMaskRelationRule::New(); + auto maskOfStatisticsPredicate = maskRule->GetDestinationsDetector(statistic); + auto maskCandidates = this->GetDataStorage()->GetSubset(maskOfStatisticsPredicate); + + if (imageCandidates->empty()) + { + mitkThrow() << "no image found connected to statistic" << statistic << " Aborting."; + } + auto image = imageCandidates->front(); + // image: 1. hierarchy level + QmitkImageStatisticsTreeItem *imageItem; + 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 && maskCandidates->empty()) + { + auto statisticsObject = statistic->GetStatisticsForTimeStep(0); + imageItem = new QmitkImageStatisticsTreeItem(statisticsObject, m_StatisticNames, imageLabel, m_RootItem); + } + else + { + imageItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, imageLabel, m_RootItem); + } + m_RootItem->appendChild(imageItem); + dataNodeToTreeItem.emplace(image, imageItem); + } + + // mask: 2. hierarchy level (optional, only if mask exists) + QmitkImageStatisticsTreeItem *lastParent; + if (!maskCandidates->empty()) + { + auto mask = maskCandidates->front(); + 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, imageItem); + } + else + { + maskItem = new QmitkImageStatisticsTreeItem(m_StatisticNames, maskLabel, 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"; + auto statisticsItem = new QmitkImageStatisticsTreeItem( + statistic->GetStatisticsForTimeStep(i), m_StatisticNames, timeStepLabel, 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/QmitkImageStatisticsTableModel.h b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h similarity index 78% rename from Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.h rename to Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h index add0af5f48..42f309e9b4 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.h +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTreeModel.h @@ -1,113 +1,115 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#ifndef QmitkImageStatisticsTableModel_h -#define QmitkImageStatisticsTableModel_h +#ifndef QmitkImageStatisticsTreeModel_h +#define QmitkImageStatisticsTreeModel_h #include "itkSimpleFastMutexLock.h" #include "QmitkAbstractDataStorageModel.h" //MITK #include #include "mitkImageStatisticsContainer.h" +class QmitkImageStatisticsTreeItem; + /*! -\class QmitkImageStatisticsTableModel +\class QmitkImageStatisticsTreeModel Model that takes a mitk::ImageStatisticsContainer and represents it as model in context of the QT view-model-concept. */ -class MITKIMAGESTATISTICSUI_EXPORT QmitkImageStatisticsTableModel : public QmitkAbstractDataStorageModel +class MITKIMAGESTATISTICSUI_EXPORT QmitkImageStatisticsTreeModel : public QmitkAbstractDataStorageModel { Q_OBJECT public: - enum class ViewMode { - imageXStatistic, - imageXMask - }; - QmitkImageStatisticsTableModel(QObject *parent = nullptr); - virtual ~QmitkImageStatisticsTableModel(); + QmitkImageStatisticsTreeModel(QObject *parent = nullptr); + virtual ~QmitkImageStatisticsTreeModel(); void SetImageNodes(const std::vector& nodes); void SetMaskNodes(const std::vector& nodes); - void SetViewMode(ViewMode m); void Clear(); virtual Qt::ItemFlags flags(const QModelIndex &index) const override; virtual QVariant data(const QModelIndex &index, int role) const override; virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; virtual 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; - /* the function returns the statistic container and the time point indicated by the index. - If the index is not valid result.first will point to a nullptr.*/ - std::pair GetRelevantStatsticsByIndex(const QModelIndex &index) const; + /* 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 therfore easy to - understand/maintaine. */ + 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; - ViewMode m_ViewMode = ViewMode::imageXStatistic; itk::SimpleFastMutexLock m_Mutex; + QmitkImageStatisticsTreeItem *m_RootItem; + QVariant m_HeaderFirstColumn; }; -#endif // mitkQmitkImageStatisticsTableModel_h +#endif // mitkQmitkImageStatisticsTreeModel_h diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.cpp index 8ea00d15bd..4236f23516 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.cpp +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.cpp @@ -1,81 +1,83 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkImageStatisticsWidget.h" -#include "QmitkTableModelToStringConverter.h" -#include "QmitkImageStatisticsTableModel.h" +#include "QmitkStatisticsModelToStringConverter.h" +#include "QmitkImageStatisticsTreeModel.h" #include #include QmitkImageStatisticsWidget::QmitkImageStatisticsWidget(QWidget* parent) : QWidget(parent) { m_Controls.setupUi(this); - m_imageStatisticsModel = new QmitkImageStatisticsTableModel(parent); + m_imageStatisticsModel = new QmitkImageStatisticsTreeModel(parent); CreateConnections(); m_ProxyModel = new QSortFilterProxyModel(this); - m_Controls.tableViewStatistics->setEnabled(false); - m_Controls.tableViewStatistics->setModel(m_ProxyModel); + m_Controls.treeViewStatistics->setEnabled(false); + m_Controls.treeViewStatistics->setModel(m_ProxyModel); m_ProxyModel->setSourceModel(m_imageStatisticsModel); - connect(m_imageStatisticsModel, &QmitkImageStatisticsTableModel::dataAvailable, this, &QmitkImageStatisticsWidget::OnDataAvailable); + connect(m_imageStatisticsModel, &QmitkImageStatisticsTreeModel::dataAvailable, this, &QmitkImageStatisticsWidget::OnDataAvailable); + connect(m_imageStatisticsModel, + &QmitkImageStatisticsTreeModel::modelChanged, + m_Controls.treeViewStatistics, + &QTreeView::expandAll); } void QmitkImageStatisticsWidget::SetDataStorage(mitk::DataStorage* newDataStorage) { m_imageStatisticsModel->SetDataStorage(newDataStorage); - m_Controls.tableViewStatistics->resizeColumnsToContents(); // should call data in table model - m_Controls.tableViewStatistics->resizeRowsToContents(); } void QmitkImageStatisticsWidget::SetImageNodes(const std::vector& nodes) { m_imageStatisticsModel->SetImageNodes(nodes); } void QmitkImageStatisticsWidget::SetMaskNodes(const std::vector& nodes) { m_imageStatisticsModel->SetMaskNodes(nodes); } void QmitkImageStatisticsWidget::Reset() { m_imageStatisticsModel->Clear(); - m_Controls.tableViewStatistics->setEnabled(false); + m_Controls.treeViewStatistics->setEnabled(false); m_Controls.buttonCopyImageStatisticsToClipboard->setEnabled(false); } void QmitkImageStatisticsWidget::CreateConnections() { connect(m_Controls.buttonCopyImageStatisticsToClipboard, &QPushButton::clicked, this, &QmitkImageStatisticsWidget::OnClipboardButtonClicked); } void QmitkImageStatisticsWidget::OnDataAvailable() { m_Controls.buttonCopyImageStatisticsToClipboard->setEnabled(true); - m_Controls.tableViewStatistics->setEnabled(true); + m_Controls.treeViewStatistics->setEnabled(true); } void QmitkImageStatisticsWidget::OnClipboardButtonClicked() { - QmitkTableModelToStringConverter converter; + QmitkStatisticsModelToStringConverter converter; converter.SetTableModel(m_imageStatisticsModel); + converter.SetRootIndex(m_Controls.treeViewStatistics->rootIndex()); converter.SetIncludeHeaderData(true); - converter.SetColumnDelimiter('\t'); QString clipboardAsString = converter.GetString(); QApplication::clipboard()->setText(clipboardAsString, QClipboard::Clipboard); } diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.h b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.h index 94ed1387f8..2805fc717f 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.h +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.h @@ -1,57 +1,57 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkImageStatisticsWidget_H__INCLUDED #define QmitkImageStatisticsWidget_H__INCLUDED #include #include #include #include class QSortFilterProxyModel; -class QmitkImageStatisticsTableModel; +class QmitkImageStatisticsTreeModel; class MITKIMAGESTATISTICSUI_EXPORT QmitkImageStatisticsWidget : public QWidget { Q_OBJECT public: QmitkImageStatisticsWidget(QWidget *parent = nullptr); /**Documentation Set the data storage the model should fetch its statistic objects from. @pre data storage must be valid */ void SetDataStorage(mitk::DataStorage *newDataStorage); void SetImageNodes(const std::vector &nodes); void SetMaskNodes(const std::vector &nodes); void Reset(); private: void CreateConnections(); void OnDataAvailable(); /** \brief Saves the image statistics to the clipboard */ void OnClipboardButtonClicked(); private: Ui::QmitkImageStatisticsControls m_Controls; - QmitkImageStatisticsTableModel *m_imageStatisticsModel; + QmitkImageStatisticsTreeModel *m_imageStatisticsModel; QSortFilterProxyModel *m_ProxyModel; }; #endif // QmitkImageStatisticsWidget_H__INCLUDED diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.ui b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.ui index 7f0984bed8..db87e06c53 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.ui +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsWidget.ui @@ -1,114 +1,92 @@ QmitkImageStatisticsControls 0 0 395 366 0 0 Form 2 2 2 2 - - - - 0 - 0 - - - - - 0 - 150 - - - - QAbstractItemView::NoEditTriggers - - - true - - - false - - + 0 0 0 0 0 false 0 0 Copy to Clipboard Qt::Horizontal 40 20 diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkStatisticsModelToStringConverter.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkStatisticsModelToStringConverter.cpp new file mode 100644 index 0000000000..b9a49a52a7 --- /dev/null +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkStatisticsModelToStringConverter.cpp @@ -0,0 +1,110 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkStatisticsModelToStringConverter.h" +#include "mitkExceptionMacro.h" + +QmitkStatisticsModelToStringConverter::QmitkStatisticsModelToStringConverter() {} + +void QmitkStatisticsModelToStringConverter::SetTableModel(QAbstractItemModel *model) +{ + m_statisticsModel = model; +} + +void QmitkStatisticsModelToStringConverter::SetRootIndex(QModelIndex rootIndex) +{ + m_rootIndex = rootIndex; +} + +QString QmitkStatisticsModelToStringConverter::GetString() const +{ + if (m_statisticsModel == nullptr) + { + mitkThrow() << "Cannot convert TableModel to String: TableModel is nullptr"; + } + + QString textData; + int columns = m_statisticsModel->columnCount(); + + if (m_includeHeaderData) + { + for (int i = 0; i < columns; i++) + { + if (i > 0) + { + textData += m_columnDelimiterWithSpace; + } + textData += m_statisticsModel->headerData(i, Qt::Horizontal).toString(); + } + textData += m_lineDelimiter; + } + textData += Iterate(m_rootIndex, m_statisticsModel); + + return textData; +} + +void QmitkStatisticsModelToStringConverter::SetRowDelimiter(QChar lineDelimiter) +{ + m_lineDelimiter = lineDelimiter; +} + +void QmitkStatisticsModelToStringConverter::SetColumnDelimiter(QChar columnDelimiter) +{ + m_columnDelimiterWithSpace = columnDelimiter + QString(" "); +} + +void QmitkStatisticsModelToStringConverter::SetIncludeHeaderData(bool includeHeaderData) +{ + m_includeHeaderData = includeHeaderData; +} + +QString QmitkStatisticsModelToStringConverter::Iterate(const QModelIndex &index, + const QAbstractItemModel *model, + int depth) const +{ + QString content; + if (index.isValid()) + { + content = index.data().toString(); + } + if (!model->hasChildren(index) || (index.flags() & Qt::ItemNeverHasChildren)) + { + return content; + } + else + { + content += m_lineDelimiter; + } + + auto rows = model->rowCount(index); + auto cols = model->columnCount(index); + for (int i = 0; i < rows; ++i) + { + if (i > 0) + { + content += m_lineDelimiter; + } + for (int j = 0; j < cols; ++j) + { + if (j > 0) + { + content += m_columnDelimiterWithSpace; + } + content += Iterate(model->index(i, j, index), model, depth + 1); + } + } + return content; +} diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.h b/Modules/ImageStatisticsUI/Qmitk/QmitkStatisticsModelToStringConverter.h similarity index 54% rename from Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.h rename to Modules/ImageStatisticsUI/Qmitk/QmitkStatisticsModelToStringConverter.h index 69be6b605b..445989e5d8 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.h +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkStatisticsModelToStringConverter.h @@ -1,67 +1,68 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkTableModelToQString_H__INCLUDED #define QmitkTableModelToQString_H__INCLUDED #include #include /** -\brief Converts the content of a table model to a string +\brief Converts the content of the statistics model to a string \details -The content of a table model is converted (as-is or transposed) to a string. Each cell of the table -is converted to a string. Default oder: iteration over rows. The line separation delimiter (default: -'\n' and the column separation delimiter (default: ',') can be chosen. It also can be chosen if -the (colum/row) headers should be exported to the string. +The content of a table model is converted to a string. Each cell is converted to a string. Default +oder: iteration over rows. The line separation delimiter (default: '\n') and the column separation +delimiter (default: ',') can be chosen. It also can be chosen if the headers should +be exported to the string. By default, the produced string is csv conform */ -class MITKIMAGESTATISTICSUI_EXPORT QmitkTableModelToStringConverter +class MITKIMAGESTATISTICSUI_EXPORT QmitkStatisticsModelToStringConverter { - public: - QmitkTableModelToStringConverter(); + QmitkStatisticsModelToStringConverter(); - void SetTableModel(QAbstractItemModel* model); + void SetTableModel(QAbstractItemModel *model); + void SetRootIndex(QModelIndex rootIndex); /** \brief Returns the string \exception throws exception if model is nullptr */ QString GetString() const; - /** - \brief If the table should be transposed (iterate over columns instead of rows) - */ - void SetTransposeOutput(bool transposeOutput); - void SetLineDelimiter(QChar lineDelimiter); + void SetRowDelimiter(QChar lineDelimiter); void SetColumnDelimiter(QChar columnDelimiter); /** \brief If header data (column/row captions) are exported */ void SetIncludeHeaderData(bool includeHeaderData); private: - QString GetStringTransposed() const; - QString GetStringNonTransposed() const; + /** +\brief Iterates recursively over all cells and returns their content +\details based on https://stackoverflow.com/questions/39153835/how-to-loop-over-qabstractitemview-indexes +*/ + QString Iterate(const QModelIndex &index, + const QAbstractItemModel *model, + int depth = 0) const; - QAbstractItemModel* m_tableModel=nullptr; - bool m_transposeOutput=false; + QAbstractItemModel *m_statisticsModel = nullptr; + QModelIndex m_rootIndex; QChar m_lineDelimiter = '\n'; bool m_includeHeaderData = false; QString m_columnDelimiterWithSpace = ", "; }; #endif // QmitkTableModelToQString_H__INCLUDED diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.cpp deleted file mode 100644 index 9d7a9a7dff..0000000000 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#include "QmitkTableModelToStringConverter.h" - -#include - -QmitkTableModelToStringConverter::QmitkTableModelToStringConverter() -{ -} - -void QmitkTableModelToStringConverter::SetTableModel(QAbstractItemModel* model) -{ - m_tableModel = model; -} - -QString QmitkTableModelToStringConverter::GetString() const -{ - if (m_tableModel == nullptr) { - mitkThrow() << "Cannot convert TableModel to String: TableModel is nullptr"; - } - - if (m_transposeOutput) { - return GetStringTransposed(); - } - else { - return GetStringNonTransposed(); - } -} - -void QmitkTableModelToStringConverter::SetTransposeOutput(bool transposeOutput) -{ - m_transposeOutput = transposeOutput; -} - -void QmitkTableModelToStringConverter::SetLineDelimiter(QChar lineDelimiter) -{ - m_lineDelimiter = lineDelimiter; -} - -void QmitkTableModelToStringConverter::SetColumnDelimiter(QChar columnDelimiter) -{ - m_columnDelimiterWithSpace = columnDelimiter + QString(" "); -} - -void QmitkTableModelToStringConverter::SetIncludeHeaderData(bool includeHeaderData) -{ - m_includeHeaderData = includeHeaderData; -} - -QString QmitkTableModelToStringConverter::GetStringTransposed() const -{ - QString textData; - int rows = m_tableModel->rowCount(); - int columns = m_tableModel->columnCount(); - - if (m_includeHeaderData) { - textData += " "; - for (int i = 0; i < rows; i++) { - textData += m_columnDelimiterWithSpace; - textData += m_tableModel->headerData(i, Qt::Vertical).toString(); - } - textData += m_lineDelimiter; - } - - for (int i = 0; i < columns; i++) { - if (i > 0) { - textData += m_lineDelimiter; - } - if (m_includeHeaderData) { - textData += m_tableModel->headerData(i, Qt::Horizontal).toString() + m_columnDelimiterWithSpace; - } - for (int j = 0; j < rows; j++) { - if (j > 0) { - textData += m_columnDelimiterWithSpace; - } - textData += m_tableModel->data(m_tableModel->index(j, i)).toString(); - MITK_WARN << i << " " << j; - } - } - return textData; -} - -QString QmitkTableModelToStringConverter::GetStringNonTransposed() const -{ - QString textData; - int rows = m_tableModel->rowCount(); - int columns = m_tableModel->columnCount(); - - if (m_includeHeaderData) { - textData += " "; - for (int i = 0; i < columns; i++) { - textData += m_columnDelimiterWithSpace; - textData += m_tableModel->headerData(i, Qt::Horizontal).toString(); - } - textData += m_lineDelimiter; - } - - for (int i = 0; i < rows; i++) { - if (i > 0) { - textData += m_lineDelimiter; - } - if (m_includeHeaderData) { - textData += m_tableModel->headerData(i, Qt::Vertical).toString() + m_columnDelimiterWithSpace; - } - for (int j = 0; j < columns; j++) { - if (j > 0) { - textData += m_columnDelimiterWithSpace; - } - textData += m_tableModel->data(m_tableModel->index(i, j)).toString(); - } - } - return textData; -} diff --git a/Modules/ImageStatisticsUI/files.cmake b/Modules/ImageStatisticsUI/files.cmake index e6faafc5b1..ef1f9c422c 100644 --- a/Modules/ImageStatisticsUI/files.cmake +++ b/Modules/ImageStatisticsUI/files.cmake @@ -1,29 +1,31 @@ set(CPP_FILES Qmitk/QmitkHistogramVisualizationWidget.cpp Qmitk/QmitkIntensityProfileVisualizationWidget.cpp - Qmitk/QmitkImageStatisticsTableModel.cpp + Qmitk/QmitkImageStatisticsTreeModel.cpp Qmitk/QmitkImageStatisticsCalculationJob.cpp - Qmitk/QmitkTableModelToStringConverter.cpp + Qmitk/QmitkStatisticsModelToStringConverter.cpp Qmitk/QmitkImageStatisticsWidget.cpp + Qmitk/QmitkImageStatisticsTreeItem.cpp ) set(H_FILES - Qmitk/QmitkTableModelToStringConverter.h + Qmitk/QmitkStatisticsModelToStringConverter.h + Qmitk/QmitkImageStatisticsTreeItem.h ) set(TPP_FILES ) set(UI_FILES Qmitk/QmitkHistogramVisualizationWidget.ui Qmitk/QmitkIntensityProfileVisualizationWidget.ui Qmitk/QmitkImageStatisticsWidget.ui ) set(MOC_H_FILES Qmitk/QmitkHistogramVisualizationWidget.h Qmitk/QmitkIntensityProfileVisualizationWidget.h - Qmitk/QmitkImageStatisticsTableModel.h + Qmitk/QmitkImageStatisticsTreeModel.h Qmitk/QmitkImageStatisticsCalculationJob.h Qmitk/QmitkImageStatisticsWidget.h ) diff --git a/Modules/ModelFit/src/Common/mitkMaskedDynamicImageStatisticsGenerator.cpp b/Modules/ModelFit/src/Common/mitkMaskedDynamicImageStatisticsGenerator.cpp index 60b95e0177..5fc4936c1c 100644 --- a/Modules/ModelFit/src/Common/mitkMaskedDynamicImageStatisticsGenerator.cpp +++ b/Modules/ModelFit/src/Common/mitkMaskedDynamicImageStatisticsGenerator.cpp @@ -1,197 +1,197 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkMaskedDynamicImageStatisticsGenerator.h" #include "mitkImageTimeSelector.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "itkMaskedNaryStatisticsImageFilter.h" mitk::MaskedDynamicImageStatisticsGenerator::MaskedDynamicImageStatisticsGenerator() { m_Mask = NULL; m_DynamicImage = NULL; }; mitk::MaskedDynamicImageStatisticsGenerator::~MaskedDynamicImageStatisticsGenerator(){}; const mitk::MaskedDynamicImageStatisticsGenerator::ResultType& mitk::MaskedDynamicImageStatisticsGenerator::GetMaximum() { if(this->HasOutdatedResults()) { CheckValidInputs(); Generate(); } return m_Maximum; }; const mitk::MaskedDynamicImageStatisticsGenerator::ResultType& mitk::MaskedDynamicImageStatisticsGenerator::GetMinimum() { if(this->HasOutdatedResults()) { CheckValidInputs(); Generate(); } return m_Minimum; }; const mitk::MaskedDynamicImageStatisticsGenerator::ResultType& mitk::MaskedDynamicImageStatisticsGenerator::GetMean() { if(this->HasOutdatedResults()) { CheckValidInputs(); Generate(); } return m_Mean; }; const mitk::MaskedDynamicImageStatisticsGenerator::ResultType& mitk::MaskedDynamicImageStatisticsGenerator::GetSigma() { if(this->HasOutdatedResults()) { CheckValidInputs(); Generate(); } return m_Sigma; }; const mitk::MaskedDynamicImageStatisticsGenerator::ResultType& mitk::MaskedDynamicImageStatisticsGenerator::GetVariance() { if(this->HasOutdatedResults()) { CheckValidInputs(); Generate(); } return m_Variance; }; const mitk::MaskedDynamicImageStatisticsGenerator::ResultType& mitk::MaskedDynamicImageStatisticsGenerator::GetSum() { if(this->HasOutdatedResults()) { CheckValidInputs(); Generate(); } return m_Sum; }; template void mitk::MaskedDynamicImageStatisticsGenerator::DoCalculateStatistics(const itk::Image* /*image*/) { typedef itk::Image InputFrameImageType; typedef itk::MaskedNaryStatisticsImageFilter FilterType; typename FilterType::Pointer statFilter = FilterType::New(); //add the time frames to the fit filter unsigned int timeSteps = this->m_DynamicImage->GetTimeSteps(); std::vector frameCache; + mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); + imageTimeSelector->SetInput(this->m_DynamicImage); for (unsigned int i = 0; i < timeSteps; ++i) { typename InputFrameImageType::Pointer frameImage; - mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); - imageTimeSelector->SetInput(this->m_DynamicImage); imageTimeSelector->SetTimeNr(i); imageTimeSelector->UpdateLargestPossibleRegion(); Image::Pointer frameMITKImage = imageTimeSelector->GetOutput(); frameCache.push_back(frameMITKImage); mitk::CastToItkImage(frameMITKImage, frameImage); statFilter->SetInput(i,frameImage); } if (this->m_InternalMask.IsNotNull()) { statFilter->SetMask(this->m_InternalMask); } statFilter->Update(); m_Maximum.SetSize(timeSteps); m_Minimum.SetSize(timeSteps); m_Mean.SetSize(timeSteps); m_Sigma.SetSize(timeSteps); m_Variance.SetSize(timeSteps); m_Sum.SetSize(timeSteps); for (unsigned int i = 0; i < timeSteps; ++i) { m_Maximum.SetElement(i,statFilter->GetMaximum()[i]); m_Minimum.SetElement(i,statFilter->GetMinimum()[i]); m_Mean.SetElement(i,statFilter->GetMean()[i]); m_Sigma.SetElement(i,statFilter->GetSigma()[i]); m_Variance.SetElement(i,statFilter->GetVariance()[i]); m_Sum.SetElement(i,statFilter->GetSum()[i]); } this->m_GenerationTimeStamp.Modified(); } void mitk::MaskedDynamicImageStatisticsGenerator::Generate() { if(this->m_Mask.IsNotNull()) { InternalMaskType::Pointer castedMask; CastToItkImage(m_Mask, castedMask); if (castedMask.IsNull()) { mitkThrow() << "Dynamic cast of mask went wrong. Internal Mask is NULL. Image statistics cannot be generated."; } this->m_InternalMask = castedMask; } else { this->m_InternalMask = NULL; } AccessFixedDimensionByItk(m_DynamicImage, mitk::MaskedDynamicImageStatisticsGenerator::DoCalculateStatistics, 4); } void mitk::MaskedDynamicImageStatisticsGenerator::CheckValidInputs() const { if (m_DynamicImage.IsNull()) { mitkThrow() << "Cannot generate statistics. Input dynamic image is not set."; } } bool mitk::MaskedDynamicImageStatisticsGenerator::HasOutdatedResults() const { bool result = this->GetMTime() > this->m_GenerationTimeStamp; if (m_DynamicImage.IsNotNull()) { if (m_DynamicImage->GetMTime() > this->m_GenerationTimeStamp) { result = true; } } if (m_Mask.IsNotNull()) { if (m_Mask->GetMTime() > this->m_GenerationTimeStamp) { result = true; } } return result; }; diff --git a/Modules/ModelFit/src/Common/mitkPixelBasedParameterFitImageGenerator.cpp b/Modules/ModelFit/src/Common/mitkPixelBasedParameterFitImageGenerator.cpp index a2046d8e69..e907d2934e 100644 --- a/Modules/ModelFit/src/Common/mitkPixelBasedParameterFitImageGenerator.cpp +++ b/Modules/ModelFit/src/Common/mitkPixelBasedParameterFitImageGenerator.cpp @@ -1,279 +1,279 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "itkCommand.h" #include "itkMultiOutputNaryFunctorImageFilter.h" #include "mitkPixelBasedParameterFitImageGenerator.h" #include "mitkImageTimeSelector.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkModelFitFunctorPolicy.h" #include "mitkExtractTimeGrid.h" void mitk::PixelBasedParameterFitImageGenerator:: onFitProgressEvent(::itk::Object* caller, const ::itk::EventObject& /*eventObject*/) { this->InvokeEvent(::itk::ProgressEvent()); auto* process = dynamic_cast(caller); if (process) { this->m_Progress = process->GetProgress(); } }; template void mitk::PixelBasedParameterFitImageGenerator::DoPrepareMask(itk::Image* image) { m_InternalMask = dynamic_cast(image); if (m_InternalMask.IsNull()) { MITK_INFO << "Parameter Fit Generator. Need to cast mask for parameter fit."; using InputImageType = itk::Image; using CastFilterType = itk::CastImageFilter< InputImageType, InternalMaskType >; typename CastFilterType::Pointer spImageCaster = CastFilterType::New(); spImageCaster->SetInput(image); m_InternalMask = spImageCaster->GetOutput(); spImageCaster->Update(); } } template mitk::PixelBasedParameterFitImageGenerator::ParameterImageMapType StoreResultImages( mitk::ModelFitFunctorBase::ParameterNamesType ¶mNames, itk::ImageSource* source, mitk::ModelFitFunctorBase::ParameterNamesType::size_type startPos, mitk::ModelFitFunctorBase::ParameterNamesType::size_type& endPos ) { mitk::PixelBasedParameterFitImageGenerator::ParameterImageMapType result; for (mitk::ModelFitFunctorBase::ParameterNamesType::size_type j = 0; j < paramNames.size(); ++j) { if (source->GetNumberOfOutputs() < startPos+j) { mitkThrow() << "Error while generating fitted parameter images. Number of sources is too low and does not match expected parameter number. Output size: "<< source->GetNumberOfOutputs()<<"; number of param names: "<GetOutput(startPos+j); mitk::CastToMitkImage(outputImg, paramImage); result.insert(std::make_pair(paramNames[j],paramImage)); } endPos = startPos + paramNames.size(); return result; } template void mitk::PixelBasedParameterFitImageGenerator::DoParameterFit(itk::Image* /*image*/) { using InputFrameImageType = itk::Image; using ParameterImageType = itk::Image; using FitFilterType = itk::MultiOutputNaryFunctorImageFilter; typename FitFilterType::Pointer fitFilter = FitFilterType::New(); typename ::itk::MemberCommand::Pointer spProgressCommand = ::itk::MemberCommand::New(); spProgressCommand->SetCallbackFunction(this, &Self::onFitProgressEvent); fitFilter->AddObserver(::itk::ProgressEvent(), spProgressCommand); //add the time frames to the fit filter + mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); + imageTimeSelector->SetInput(this->m_DynamicImage); std::vector frameCache; for (unsigned int i = 0; i < this->m_DynamicImage->GetTimeSteps(); ++i) { typename InputFrameImageType::Pointer frameImage; - mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); - imageTimeSelector->SetInput(this->m_DynamicImage); imageTimeSelector->SetTimeNr(i); imageTimeSelector->UpdateLargestPossibleRegion(); Image::Pointer frameMITKImage = imageTimeSelector->GetOutput(); frameCache.push_back(frameMITKImage); mitk::CastToItkImage(frameMITKImage, frameImage); fitFilter->SetInput(i,frameImage); } ModelBaseType::TimeGridType timeGrid = ExtractTimeGrid(m_DynamicImage); if (m_TimeGridByParameterizer) { if (timeGrid.GetSize() != m_ModelParameterizer->GetDefaultTimeGrid().GetSize()) { mitkThrow() << "Cannot do fitting. Filter is set to use default time grid of the parameterizer, but grid size does not match the number of input image frames. Grid size: " << m_ModelParameterizer->GetDefaultTimeGrid().GetSize() << "; frame count: " << timeGrid.GetSize(); } } else { this->m_ModelParameterizer->SetDefaultTimeGrid(timeGrid); } ModelFitFunctorPolicy functor; functor.SetModelFitFunctor(this->m_FitFunctor); functor.SetModelParameterizer(this->m_ModelParameterizer); fitFilter->SetFunctor(functor); if (this->m_InternalMask.IsNotNull()) { fitFilter->SetMask(this->m_InternalMask); } //generate the fits fitFilter->Update(); //convert the outputs into mitk images and fill the parameter image map ModelBaseType::Pointer refModel = this->m_ModelParameterizer->GenerateParameterizedModel(); ModelFitFunctorBase::ParameterNamesType paramNames = refModel->GetParameterNames(); ModelFitFunctorBase::ParameterNamesType derivedParamNames = refModel->GetDerivedParameterNames(); ModelFitFunctorBase::ParameterNamesType criterionNames = this->m_FitFunctor->GetCriterionNames(); ModelFitFunctorBase::ParameterNamesType evaluationParamNames = this->m_FitFunctor->GetEvaluationParameterNames(); ModelFitFunctorBase::ParameterNamesType debugParamNames = this->m_FitFunctor->GetDebugParameterNames(); if (fitFilter->GetNumberOfOutputs() != (paramNames.size() + derivedParamNames.size() + criterionNames.size() + evaluationParamNames.size() + debugParamNames.size())) { mitkThrow() << "Error while generating fitted parameter images. Fit filter output size does not match expected parameter number. Output size: "<< fitFilter->GetNumberOfOutputs(); } ModelFitFunctorBase::ParameterNamesType::size_type resultPos = 0; this->m_TempResultMap = StoreResultImages(paramNames,fitFilter,resultPos, resultPos); this->m_TempDerivedResultMap = StoreResultImages(derivedParamNames,fitFilter,resultPos, resultPos); this->m_TempCriterionResultMap = StoreResultImages(criterionNames,fitFilter,resultPos, resultPos); this->m_TempEvaluationResultMap = StoreResultImages(evaluationParamNames,fitFilter,resultPos, resultPos); //also add debug params (if generated) to the evaluation result map mitk::PixelBasedParameterFitImageGenerator::ParameterImageMapType debugMap = StoreResultImages(debugParamNames, fitFilter, resultPos, resultPos); this->m_TempEvaluationResultMap.insert(debugMap.begin(), debugMap.end()); } bool mitk::PixelBasedParameterFitImageGenerator::HasOutdatedResult() const { bool result = Superclass::HasOutdatedResult(); if (m_ModelParameterizer.IsNotNull()) { if (m_ModelParameterizer->GetMTime() > this->m_GenerationTimeStamp) { result = true; } } if (m_FitFunctor.IsNotNull()) { if (m_FitFunctor->GetMTime() > this->m_GenerationTimeStamp) { result = true; } } if (m_DynamicImage.IsNotNull()) { if (m_DynamicImage->GetMTime() > this->m_GenerationTimeStamp) { result = true; } } if (m_Mask.IsNotNull()) { if (m_Mask->GetMTime() > this->m_GenerationTimeStamp) { result = true; } } return result; }; void mitk::PixelBasedParameterFitImageGenerator::CheckValidInputs() const { Superclass::CheckValidInputs(); if (m_DynamicImage.IsNull()) { mitkThrow() << "Cannot generate fitted parameter images. Input dynamic image is not set."; } }; void mitk::PixelBasedParameterFitImageGenerator::DoFitAndGetResults(ParameterImageMapType& parameterImages, ParameterImageMapType& derivedParameterImages, ParameterImageMapType& criterionImages, ParameterImageMapType& evaluationParameterImages) { this->m_Progress = 0; if(this->m_Mask.IsNotNull()) { AccessFixedDimensionByItk(m_Mask, mitk::PixelBasedParameterFitImageGenerator::DoPrepareMask, 3); } else { this->m_InternalMask = nullptr; } AccessFixedDimensionByItk(m_DynamicImage, mitk::PixelBasedParameterFitImageGenerator::DoParameterFit, 4); parameterImages = this->m_TempResultMap; derivedParameterImages = this->m_TempDerivedResultMap; criterionImages = this->m_TempCriterionResultMap; evaluationParameterImages = this->m_TempEvaluationResultMap; }; double mitk::PixelBasedParameterFitImageGenerator::GetProgress() const { return m_Progress; }; mitk::PixelBasedParameterFitImageGenerator::ParameterNamesType mitk::PixelBasedParameterFitImageGenerator::GetParameterNames() const { ParameterizerType::ModelBasePointer parameterizedModel = m_ModelParameterizer->GenerateParameterizedModel(); return parameterizedModel->GetParameterNames(); } mitk::PixelBasedParameterFitImageGenerator::ParameterNamesType mitk::PixelBasedParameterFitImageGenerator::GetDerivedParameterNames() const { ParameterizerType::ModelBasePointer parameterizedModel = m_ModelParameterizer->GenerateParameterizedModel(); return parameterizedModel->GetDerivedParameterNames(); } mitk::PixelBasedParameterFitImageGenerator::ParameterNamesType mitk::PixelBasedParameterFitImageGenerator::GetCriterionNames() const { return this->m_FitFunctor->GetCriterionNames(); } mitk::PixelBasedParameterFitImageGenerator::ParameterNamesType mitk::PixelBasedParameterFitImageGenerator::GetEvaluationParameterNames() const { auto evals = this->m_FitFunctor->GetEvaluationParameterNames(); auto debugs = this->m_FitFunctor->GetDebugParameterNames(); evals.insert(evals.end(), debugs.begin(), debugs.end()); return evals; } diff --git a/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp b/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp index 5f77a32368..2f25f4aeef 100644 --- a/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp +++ b/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.cpp @@ -1,109 +1,121 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Software Development for Integrated Diagnostic and Therapy. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #include "QmitkParameterFitBackgroundJob.h" #include "mitkModelFitInfo.h" void ParameterFitBackgroundJob::OnFitEvent(::itk::Object* caller, const itk::EventObject & event) { itk::ProgressEvent progressEvent; itk::InitializeEvent initEvent; itk::StartEvent startEvent; itk::EndEvent endEvent; if (progressEvent.CheckEvent(&event)) { mitk::ParameterFitImageGeneratorBase* castedReporter = dynamic_cast(caller); emit JobProgress(castedReporter->GetProgress()); } else if (initEvent.CheckEvent(&event)) { emit JobStatusChanged(QString("Initializing parameter fit generator")); } else if (startEvent.CheckEvent(&event)) { emit JobStatusChanged(QString("Started fitting process.")); } else if (endEvent.CheckEvent(&event)) { emit JobStatusChanged(QString("Finished fitting process.")); } } ParameterFitBackgroundJob:: -ParameterFitBackgroundJob(mitk::ParameterFitImageGeneratorBase* generator, const mitk::modelFit::ModelFitInfo* fitInfo, mitk::DataNode* parentNode) +ParameterFitBackgroundJob(mitk::ParameterFitImageGeneratorBase* generator, const mitk::modelFit::ModelFitInfo* fitInfo, mitk::DataNode* parentNode) : ParameterFitBackgroundJob(generator, fitInfo, parentNode, {}) +{ +}; + +ParameterFitBackgroundJob:: +ParameterFitBackgroundJob(mitk::ParameterFitImageGeneratorBase* generator, const mitk::modelFit::ModelFitInfo* fitInfo, mitk::DataNode* parentNode, mitk::modelFit::ModelFitResultNodeVectorType additionalRelevantNodes) { if (!generator) { mitkThrow() << "Cannot create parameter fit background job. Passed fit generator is NULL."; } if (!fitInfo) { mitkThrow() << "Cannot create parameter fit background job. Passed model traits interface is NULL."; } m_Generator = generator; m_ModelFitInfo = fitInfo; m_ParentNode = parentNode; + m_AdditionalRelevantNodes = additionalRelevantNodes; m_spCommand = ::itk::MemberCommand::New(); m_spCommand->SetCallbackFunction(this, &ParameterFitBackgroundJob::OnFitEvent); m_ObserverID = m_Generator->AddObserver(::itk::AnyEvent(), m_spCommand); }; mitk::DataNode* ParameterFitBackgroundJob:: GetParentNode() const { return m_ParentNode; }; +mitk::modelFit::ModelFitResultNodeVectorType ParameterFitBackgroundJob::GetAdditionalRelevantNodes() const +{ + return m_AdditionalRelevantNodes; +}; + + ParameterFitBackgroundJob:: ~ParameterFitBackgroundJob() { m_Generator->RemoveObserver(m_ObserverID); }; void ParameterFitBackgroundJob:: run() { try { emit JobStatusChanged(QString("Started fit session.Generate UID: ")+QString::fromStdString(m_ModelFitInfo->uid)); m_Generator->Generate(); emit JobStatusChanged(QString("Generate result nodes.")); m_Results = mitk::modelFit::CreateResultNodeMap(m_Generator->GetParameterImages(), m_Generator->GetDerivedParameterImages(), m_Generator->GetCriterionImages(), m_Generator->GetEvaluationParameterImages(), m_ModelFitInfo); emit ResultsAreAvailable(m_Results, this); } catch (::std::exception& e) { emit Error(QString("Error while fitting data. Details: ")+QString::fromLatin1(e.what())); } catch (...) { emit Error(QString("Unkown error when fitting the data.")); } emit Finished(); }; diff --git a/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.h b/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.h index 0cb5b505d3..17dda9b0fe 100644 --- a/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.h +++ b/Modules/ModelFitUI/Qmitk/QmitkParameterFitBackgroundJob.h @@ -1,78 +1,84 @@ /*========================================================================= Program: Medical Imaging & Interaction Toolkit Language: C++ Date: $Date$ Version: $Revision$ Copyright (c) German Cancer Research Center, Software Development for Integrated Diagnostic and Therapy. All rights reserved. See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =========================================================================*/ #ifndef __QMITK_PARAMETER_FIT_BACKGROUND_JOB_H #define __QMITK_PARAMETER_FIT_BACKGROUND_JOB_H //QT #include #include //MITK #include #include #include #include // ITK #include #include "MitkModelFitUIExports.h" class MITKMODELFITUI_EXPORT ParameterFitBackgroundJob : public QObject, public QRunnable { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: ParameterFitBackgroundJob(mitk::ParameterFitImageGeneratorBase* generator, const mitk::modelFit::ModelFitInfo* fitInfo, mitk::DataNode* parentNode = NULL); - ~ParameterFitBackgroundJob(); + /** */ + ParameterFitBackgroundJob(mitk::ParameterFitImageGeneratorBase* generator, const mitk::modelFit::ModelFitInfo* fitInfo, mitk::DataNode* parentNode, mitk::modelFit::ModelFitResultNodeVectorType additionalRelevantNodes); + + ~ParameterFitBackgroundJob(); void run(); /**Returns the node (if defined), that is the parent object for the results of the job. May be null.*/ mitk::DataNode* GetParentNode() const; + mitk::modelFit::ModelFitResultNodeVectorType GetAdditionalRelevantNodes() const; + signals: void Finished(); void Error(QString err); void ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType resultMap, const ParameterFitBackgroundJob* pJob); void JobProgress(double progress); void JobStatusChanged(QString info); protected: //Inputs mitk::ParameterFitImageGeneratorBase::Pointer m_Generator; mitk::modelFit::ModelFitInfo::ConstPointer m_ModelFitInfo; mitk::DataNode::Pointer m_ParentNode; + mitk::modelFit::ModelFitResultNodeVectorType m_AdditionalRelevantNodes; // Results mitk::modelFit::ModelFitResultNodeVectorType m_Results; ::itk::MemberCommand::Pointer m_spCommand; unsigned long m_ObserverID; void OnFitEvent(::itk::Object *, const itk::EventObject &event); }; #endif diff --git a/Modules/OpenIGTLink/mitkIGTLDummyMessage.cpp b/Modules/OpenIGTLink/mitkIGTLDummyMessage.cpp index 0414cc41bd..6dff7d53c4 100644 --- a/Modules/OpenIGTLink/mitkIGTLDummyMessage.cpp +++ b/Modules/OpenIGTLink/mitkIGTLDummyMessage.cpp @@ -1,66 +1,65 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkIGTLDummyMessage.h" #include "igtl_header.h" #include "igtl_util.h" mitk::IGTLDummyMessage::IGTLDummyMessage() : StringMessage() { - this->m_DefaultBodyType = "DUMMY"; } mitk::IGTLDummyMessage::~IGTLDummyMessage() { } void mitk::IGTLDummyMessage::SetDummyString( const std::string& dummyString ) { this->m_dummyString = dummyString; this->m_String = "This is a dummy string"; } std::string mitk::IGTLDummyMessage::GetDummyString() { return this->m_dummyString; } igtl::MessageBase::Pointer mitk::IGTLDummyMessage::Clone() { //initialize the clone mitk::IGTLDummyMessage::Pointer clone = mitk::IGTLDummyMessage::New(); //copy the data clone->SetString(this->GetString()); clone->SetDummyString(this->GetDummyString()); return igtl::MessageBase::Pointer(clone.GetPointer()); } /** * \brief Clones the original message interpreted as transform message * \param original_ The original message that will be interpreted as transform * message * \return The clone of the input message */ igtl::MessageBase::Pointer mitk::DummyMsgCloneHandler::Clone(igtl::MessageBase* original_) { mitk::IGTLDummyMessage* original = (mitk::IGTLDummyMessage*)original_; return original->Clone(); } diff --git a/Modules/OpenIGTLink/mitkIGTLDummyMessage.h b/Modules/OpenIGTLink/mitkIGTLDummyMessage.h index 5a28cebf96..2f99e1e58a 100644 --- a/Modules/OpenIGTLink/mitkIGTLDummyMessage.h +++ b/Modules/OpenIGTLink/mitkIGTLDummyMessage.h @@ -1,74 +1,71 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKIGTLDUMMYMESSAGE_H #define MITKIGTLDUMMYMESSAGE_H #include "MitkOpenIGTLinkExports.h" #include "igtlObject.h" #include "igtlStringMessage.h" #include "mitkIGTLMessageCloneHandler.h" namespace mitk { -/** Documentation -* \class IGTLDummyMessage -* \brief This class is a dummy message to show the how to implement a new message -* type +/** + * \class IGTLDummyMessage + * \brief This class is a dummy message to show how to implement a new message type */ -class MITKOPENIGTLINK_EXPORT IGTLDummyMessage: public igtl::StringMessage +class MITKOPENIGTLINK_EXPORT IGTLDummyMessage : public igtl::StringMessage { public: typedef IGTLDummyMessage Self; typedef StringMessage Superclass; typedef igtl::SmartPointer Pointer; typedef igtl::SmartPointer ConstPointer; - igtlTypeMacro( mitk::IGTLDummyMessage, igtl::StringMessage ); - igtlNewMacro( mitk::IGTLDummyMessage ); + igtlTypeMacro( mitk::IGTLDummyMessage, igtl::StringMessage ) + igtlNewMacro( mitk::IGTLDummyMessage ) public: /** * Set dummy string - */ + */ void SetDummyString( const std::string& dummyString ); /** * Get dummy string - */ + */ std::string GetDummyString(); /** * Returns a clone of itself - */ - igtl::MessageBase::Pointer Clone(); + */ + igtl::MessageBase::Pointer Clone() override; protected: IGTLDummyMessage(); ~IGTLDummyMessage() override; std::string m_dummyString; }; - -mitkIGTMessageCloneClassMacro(IGTLDummyMessage, DummyMsgCloneHandler); - +mitkIGTMessageCloneClassMacro(IGTLDummyMessage, DummyMsgCloneHandler) } // namespace mitk #endif diff --git a/Modules/OpenIGTLink/mitkIGTLMessageFactory.cpp b/Modules/OpenIGTLink/mitkIGTLMessageFactory.cpp index fcf45364ec..79cdfad769 100644 --- a/Modules/OpenIGTLink/mitkIGTLMessageFactory.cpp +++ b/Modules/OpenIGTLink/mitkIGTLMessageFactory.cpp @@ -1,295 +1,294 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkIGTLMessageFactory.h" // IGT message types #include "igtlImageMessage.h" #include "igtlTransformMessage.h" #include "igtlPositionMessage.h" #include "igtlStatusMessage.h" #include "igtlImageMetaMessage.h" #include "igtlPointMessage.h" #include "igtlTrajectoryMessage.h" #include "igtlStringMessage.h" #include "igtlSensorMessage.h" #include "igtlBindMessage.h" #include "igtlPolyDataMessage.h" #include "igtlQuaternionTrackingDataMessage.h" #include "igtlCapabilityMessage.h" #include "igtlNDArrayMessage.h" #include "igtlTrackingDataMessage.h" #include "igtlColorTableMessage.h" #include "igtlLabelMetaMessage.h" //own types #include "mitkIGTLDummyMessage.h" #include "mitkIGTLMessageCommon.h" #include "itksys/SystemTools.hxx" //------------------------------------------------------------ // Define message clone classes // igtlMessageHandlerClassMacro() defines a child class of // igtl::MessageHandler to handle OpenIGTLink messages for // the message type specified as the first argument. The // second argument will be used for the name of this // message handler class, while the third argument specifies // a type of data that will be shared with the message functions // of this handler class. -mitkIGTMessageCloneClassMacro(igtl::TransformMessage, TransformMsgCloneHandler); +mitkIGTMessageCloneClassMacro(igtl::TransformMessage, TransformMsgCloneHandler) /** * \brief Clones the original message interpreted as transform message * \param original_ The original message that will be interpreted as transform * message * \return The clone of the input message */ igtl::MessageBase::Pointer TransformMsgCloneHandler::Clone(igtl::MessageBase* original_) { bool copySuccess = false; igtl::TransformMessage::Pointer clone_ = igtl::TransformMessage::New(); //initialize the clone // clone = igtl::MessageBase::New(); igtl::TransformMessage* original = (igtl::TransformMessage*)original_; //copy all meta data copySuccess = clone_->Copy(original); if (!copySuccess) return nullptr; //copy all data that is important for this class //copy the matrix igtl::Matrix4x4 mat; original->GetMatrix(mat); clone_->SetMatrix(mat); //copy the normals float normals[3][3]; original->GetNormals(normals); clone_->SetNormals(normals); //copy the position float position[3]; original->GetPosition(position); clone_->SetPosition(position); return igtl::MessageBase::Pointer(clone_.GetPointer()); } mitk::IGTLMessageFactory::IGTLMessageFactory() { //create clone handlers // mitk::IGTLMessageCloneHandler::Pointer tmch = ; this->AddMessageNewMethod("NONE", nullptr); - //OpenIGTLink Types V1 + // OpenIGTLink Types V1 this->AddMessageNewMethod("IMAGE", (PointerToMessageBaseNew)&igtl::ImageMessage::New); this->AddMessageNewMethod("TRANSFORM", (PointerToMessageBaseNew)&igtl::TransformMessage::New); this->AddMessageNewMethod("POSITION", (PointerToMessageBaseNew)&igtl::PositionMessage::New); this->AddMessageNewMethod("STATUS", (PointerToMessageBaseNew)&igtl::StatusMessage::New); this->AddMessageNewMethod("CAPABILITY", (PointerToMessageBaseNew)&igtl::CapabilityMessage::New); this->AddMessageNewMethod("GET_IMAGE", (PointerToMessageBaseNew)&igtl::GetImageMessage::New); this->AddMessageNewMethod("GET_TRANS", (PointerToMessageBaseNew)&igtl::GetTransformMessage::New); //this->AddMessageNewMethod("GET_POS", (PointerToMessageBaseNew)&igtl::GetPositionMessage::New); //not available??? this->AddMessageNewMethod("GET_STATUS", (PointerToMessageBaseNew)&igtl::GetStatusMessage::New); - this->AddMessageNewMethod("GET_CAPABIL", (PointerToMessageBaseNew)&igtl::GetCapabilityMessage::New); - // //OpenIGTLink Types V2 + // OpenIGTLink Types V2 this->AddMessageNewMethod("IMGMETA", (PointerToMessageBaseNew)&igtl::ImageMetaMessage::New); this->AddMessageNewMethod("LBMETA", (PointerToMessageBaseNew)&igtl::LabelMetaMessage::New); this->AddMessageNewMethod("COLORT", (PointerToMessageBaseNew)&igtl::ColorTableMessage::New); this->AddMessageNewMethod("POINT", (PointerToMessageBaseNew)&igtl::PointMessage::New); this->AddMessageNewMethod("TRAJ", (PointerToMessageBaseNew)&igtl::TrajectoryMessage::New); this->AddMessageNewMethod("TDATA", (PointerToMessageBaseNew)&igtl::TrackingDataMessage::New); this->AddMessageNewMethod("QTDATA", (PointerToMessageBaseNew)&igtl::QuaternionTrackingDataMessage::New); this->AddMessageNewMethod("SENSOR", (PointerToMessageBaseNew)&igtl::SensorMessage::New); this->AddMessageNewMethod("STRING", (PointerToMessageBaseNew)&igtl::StringMessage::New); this->AddMessageNewMethod("NDARRAY", (PointerToMessageBaseNew)&igtl::NDArrayMessage::New); this->AddMessageNewMethod("BIND", (PointerToMessageBaseNew)&igtl::BindMessage::New); this->AddMessageNewMethod("POLYDATA", (PointerToMessageBaseNew)&igtl::PolyDataMessage::New); this->AddMessageNewMethod("GET_IMGMETA", (PointerToMessageBaseNew)&igtl::GetImageMetaMessage::New); this->AddMessageNewMethod("GET_LBMETA", (PointerToMessageBaseNew)&igtl::GetLabelMetaMessage::New); this->AddMessageNewMethod("GET_COLORT", (PointerToMessageBaseNew)&igtl::GetColorTableMessage::New); this->AddMessageNewMethod("GET_POINT", (PointerToMessageBaseNew)&igtl::GetPointMessage::New); this->AddMessageNewMethod("GET_TRAJ", (PointerToMessageBaseNew)&igtl::GetTrajectoryMessage::New); // this->AddMessageNewMethod("GET_TDATA", (PointerToMessageBaseNew)&igtl::GetTrackingDataMessage::New); //not available??? // this->AddMessageNewMethod("GET_QTDATA", (PointerToMessageBaseNew)&igtl::GetQuaternionTrackingDataMessage::New); //not available??? // this->AddMessageNewMethod("GET_SENSOR", (PointerToMessageBaseNew)&igtl::GetSensorMessage::New); //not available??? // this->AddMessageNewMethod("GET_STRING", (PointerToMessageBaseNew)&igtl::GetStringMessage::New); //not available??? // this->AddMessageNewMethod("GET_NDARRAY", (PointerToMessageBaseNew)&igtl::GetNDArrayMessage::New); //not available??? this->AddMessageNewMethod("GET_BIND", (PointerToMessageBaseNew)&igtl::GetBindMessage::New); this->AddMessageNewMethod("GET_POLYDATA", (PointerToMessageBaseNew)&igtl::GetPolyDataMessage::New); this->AddMessageNewMethod("RTS_BIND", (PointerToMessageBaseNew)&igtl::RTSBindMessage::New); this->AddMessageNewMethod("RTS_QTDATA", (PointerToMessageBaseNew)&igtl::RTSQuaternionTrackingDataMessage::New); this->AddMessageNewMethod("RTS_TDATA", (PointerToMessageBaseNew)&igtl::RTSTrackingDataMessage::New); //todo: check if there are more RTS messages this->AddMessageNewMethod("STT_BIND", (PointerToMessageBaseNew)&igtl::StartBindMessage::New); this->AddMessageNewMethod("STT_TDATA", (PointerToMessageBaseNew)&igtl::StartTrackingDataMessage::New); this->AddMessageNewMethod("STT_QTDATA", (PointerToMessageBaseNew)&igtl::StartQuaternionTrackingDataMessage::New); //todo: check if there are more STT messages this->AddMessageNewMethod("STP_BIND", (PointerToMessageBaseNew)&igtl::StopBindMessage::New); this->AddMessageNewMethod("STP_TDATA", (PointerToMessageBaseNew)&igtl::StopTrackingDataMessage::New); this->AddMessageNewMethod("STP_QTDATA", (PointerToMessageBaseNew)&igtl::StopQuaternionTrackingDataMessage::New); //todo: check if there are more STP messages //Own Types this->AddMessageNewMethod("DUMMY", (PointerToMessageBaseNew)&mitk::IGTLDummyMessage::New); } mitk::IGTLMessageFactory::~IGTLMessageFactory() { } void mitk::IGTLMessageFactory::AddMessageType(std::string messageTypeName, IGTLMessageFactory::PointerToMessageBaseNew messageTypeNewPointer, mitk::IGTLMessageCloneHandler::Pointer cloneHandler) { this->AddMessageNewMethod(messageTypeName, messageTypeNewPointer); this->AddMessageCloneHandler(messageTypeName, cloneHandler); } void mitk::IGTLMessageFactory::AddMessageNewMethod(std::string messageTypeName, IGTLMessageFactory::PointerToMessageBaseNew messageTypeNewPointer) { this->m_NewMethods[messageTypeName] = messageTypeNewPointer; } void mitk::IGTLMessageFactory::AddMessageCloneHandler(std::string msgTypeName, mitk::IGTLMessageCloneHandler::Pointer cloneHandler) { this->m_CloneHandlers[msgTypeName] = cloneHandler; } mitk::IGTLMessageCloneHandler::Pointer mitk::IGTLMessageFactory::GetCloneHandler(std::string messageTypeName) { if (this->m_CloneHandlers.find(messageTypeName) != this->m_CloneHandlers.end()) { return m_CloneHandlers[messageTypeName]; } MITK_ERROR("IGTLMessageFactory") << messageTypeName << " message type is not registered to factory!"; mitkThrow() << messageTypeName << " message type is not registered to factory!"; return nullptr; } igtl::MessageBase::Pointer mitk::IGTLMessageFactory::Clone(igtl::MessageBase::Pointer msg) { return this->GetCloneHandler(msg->GetDeviceType())->Clone(msg); } mitk::IGTLMessageFactory::PointerToMessageBaseNew mitk::IGTLMessageFactory::GetMessageTypeNewPointer(std::string messageTypeName) { if (this->m_NewMethods.find(messageTypeName) != this->m_NewMethods.end()) { return m_NewMethods[messageTypeName]; } MITK_ERROR("IGTLMessageFactory") << messageTypeName << " message type is not registered to factory!"; return nullptr; } igtl::MessageBase::Pointer mitk::IGTLMessageFactory::CreateInstance(std::string messageTypeName) { mitk::IGTLMessageFactory::PointerToMessageBaseNew newPointer = this->GetMessageTypeNewPointer(messageTypeName); if (newPointer != nullptr) { return newPointer(); } else { return nullptr; } } std::list mitk::IGTLMessageFactory::GetAvailableMessageRequestTypes() { std::list allGetMessages; for (std::map::const_iterator it = this->m_NewMethods.begin(); it != this->m_NewMethods.end(); ++it) { if (it->first.find("GET_") != std::string::npos || it->first.find("STT_") != std::string::npos || it->first.find("STP_") != std::string::npos || it->first.find("RTS_") != std::string::npos) { allGetMessages.push_back(it->first); } } return allGetMessages; } igtl::MessageBase::Pointer mitk::IGTLMessageFactory::CreateInstance(igtl::MessageHeader::Pointer msgHeader) { std::string messageType; //check the header if (msgHeader.IsNull()) { messageType = "NONE"; } else { messageType = msgHeader->GetDeviceType(); } //make message type uppercase messageType = itksys::SystemTools::UpperCase(messageType); //find the according new method if (this->m_NewMethods.find(messageType) != this->m_NewMethods.end()) { if (this->m_NewMethods[messageType] != nullptr) { // Call tracker New() function if tracker not nullptr return (*this->m_NewMethods[messageType])(); } else return nullptr; } else { MITK_ERROR("IGTLMessageFactory") << "Unknown IGT message type: " << messageType; return nullptr; } } diff --git a/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h b/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h index 2f44a4b1a3..a10283ffe5 100644 --- a/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h +++ b/Modules/Pharmacokinetics/include/mitkConcentrationCurveGenerator.h @@ -1,146 +1,146 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef CONCENTRATIONCURVEGENERATOR_H #define CONCENTRATIONCURVEGENERATOR_H #include #include #include "mitkConvertToConcentrationAbsoluteFunctor.h" #include "mitkConvertToConcentrationRelativeFunctor.h" #include "MitkPharmacokineticsExports.h" namespace mitk { /** \class ConcentrationCurveGenerator * \brief Converts a given 4D mitk::Image with MR signal values into a 4D mitk::Image with corresponding contrast agent concentration values * * From a given 4D image, the Generator takes the 3D image of the first time point as baseline image. It then loops over all time steps, casts * the current 3D image to itk and passes it to the ConvertToconcentrationFunctor. The returned 3D image has now values of concentration type and is stored at its timepoint * in the return image. */ class MITKPHARMACOKINETICS_EXPORT ConcentrationCurveGenerator : public itk::Object { public: mitkClassMacroItkParent(ConcentrationCurveGenerator, itk::Object); itkNewMacro(Self); //typedef itk::Image ImageType; typedef itk::Image ConvertedImageType; /** Getter and Setter for 4D mitk::Image*/ - itkSetObjectMacro(DynamicImage,Image); + itkSetConstObjectMacro(DynamicImage,Image); itkGetConstObjectMacro(DynamicImage,Image); /** Parameters Relevant for conversion Calculation; Have to be Set externally (Sequence Dependend)*/ itkSetMacro(RelaxationTime, double); itkGetConstReferenceMacro(RelaxationTime, double); itkSetMacro(Relaxivity, double); itkGetConstReferenceMacro(Relaxivity, double); itkSetMacro(RecoveryTime, double); itkGetConstReferenceMacro(RecoveryTime, double); itkSetMacro(FlipAngle, double); itkGetConstReferenceMacro(FlipAngle, double); itkSetMacro(Factor, double); itkGetConstReferenceMacro(Factor, double); /** Getter and Setter for T10 Map image*/ - itkSetObjectMacro(T10Image,Image); + itkSetConstObjectMacro(T10Image,Image); itkGetConstObjectMacro(T10Image,Image); itkSetMacro(T2Factor, double); itkGetConstReferenceMacro(T2Factor, double); itkSetMacro(T2EchoTime, double); itkGetConstReferenceMacro(T2EchoTime, double); /** @brief Calls Convert and returns the 4D mitk::image in Concentration units*/ itkSetMacro(isTurboFlashSequence,bool); itkGetConstReferenceMacro(isTurboFlashSequence,bool); itkSetMacro(AbsoluteSignalEnhancement,bool); itkGetConstReferenceMacro(AbsoluteSignalEnhancement,bool); itkSetMacro(RelativeSignalEnhancement,bool); itkGetConstReferenceMacro(RelativeSignalEnhancement,bool); itkSetMacro(UsingT1Map,bool); itkGetConstReferenceMacro(UsingT1Map,bool); itkSetMacro(isT2weightedImage,bool); itkGetConstReferenceMacro(isT2weightedImage,bool); Image::Pointer GetConvertedImage(); protected: ConcentrationCurveGenerator(); ~ConcentrationCurveGenerator(); template - mitk::Image::Pointer convertToConcentration(mitk::Image::Pointer inputImage,mitk::Image::Pointer baselineImage); + mitk::Image::Pointer convertToConcentration(const mitk::Image* inputImage, const mitk::Image* baselineImage); /** Calls ConvertToconcentrationFunctor for passed 3D itk::image*/ -mitk::Image::Pointer ConvertSignalToConcentrationCurve(mitk::Image::Pointer inputImage, mitk::Image::Pointer baselineImage); + mitk::Image::Pointer ConvertSignalToConcentrationCurve(const mitk::Image* inputImage, const mitk::Image* baselineImage); /** @brief Takes the 3D image of the first timepoint to set as baseline image*/ void PrepareBaselineImage(); /** @brief loops over all timepoints, casts the current timepoint 3D mitk::image to itk and passes it to ConvertSignalToConcentrationCurve */ virtual void Convert(); private: - Image::Pointer m_DynamicImage; - Image::Pointer m_BaselineImage; - Image::Pointer m_T10Image; + Image::ConstPointer m_DynamicImage; + Image::ConstPointer m_BaselineImage; + Image::ConstPointer m_T10Image; Image::Pointer m_ConvertedImage; bool m_isT2weightedImage; bool m_isTurboFlashSequence; bool m_AbsoluteSignalEnhancement; bool m_RelativeSignalEnhancement; bool m_UsingT1Map; double m_Factor; //=Repetition Time TR double m_RecoveryTime; //= pre-CA T1 time double m_RelaxationTime; //= contrast agent relaxivity double m_Relaxivity; double m_FlipAngle; double m_T2Factor; double m_T2EchoTime; }; } #endif // CONCENTRATIONCURVEGENERATOR_H diff --git a/Modules/Pharmacokinetics/src/Common/mitkConcentrationCurveGenerator.cpp b/Modules/Pharmacokinetics/src/Common/mitkConcentrationCurveGenerator.cpp index cc3676fbd7..8ded94876d 100644 --- a/Modules/Pharmacokinetics/src/Common/mitkConcentrationCurveGenerator.cpp +++ b/Modules/Pharmacokinetics/src/Common/mitkConcentrationCurveGenerator.cpp @@ -1,265 +1,252 @@ #include "mitkConcentrationCurveGenerator.h" #include "mitkConvertToConcentrationTurboFlashFunctor.h" #include "mitkConvertT2ConcentrationFunctor.h" #include "mitkConvertToConcentrationViaT1Functor.h" #include "mitkImageTimeSelector.h" #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include "mitkModelBase.h" #include "mitkExtractTimeGrid.h" #include "mitkArbitraryTimeGeometry.h" #include "itkImageIOBase.h" #include "itkBinaryFunctorImageFilter.h" #include "itkTernaryFunctorImageFilter.h" mitk::ConcentrationCurveGenerator::ConcentrationCurveGenerator() : m_isT2weightedImage(false), m_isTurboFlashSequence(false), m_AbsoluteSignalEnhancement(false), m_RelativeSignalEnhancement(0.0), m_UsingT1Map(false), m_Factor(0.0), m_RecoveryTime(0.0), m_RelaxationTime(0.0), m_Relaxivity(0.0), m_FlipAngle(0.0), m_T2Factor(0.0), m_T2EchoTime(0.0) { } mitk::ConcentrationCurveGenerator::~ConcentrationCurveGenerator() { } mitk::Image::Pointer mitk::ConcentrationCurveGenerator::GetConvertedImage() { if(this->m_DynamicImage.IsNull()) { itkExceptionMacro( << "Dynamic Image not set!"); } else { Convert(); } return m_ConvertedImage; } void mitk::ConcentrationCurveGenerator::Convert() { mitk::Image::Pointer tempImage = mitk::Image::New(); mitk::PixelType pixeltype = mitk::MakeScalarPixelType(); tempImage->Initialize(pixeltype,*this->m_DynamicImage->GetTimeGeometry()); mitk::TimeGeometry::Pointer timeGeometry = (this->m_DynamicImage->GetTimeGeometry())->Clone(); tempImage->SetTimeGeometry(timeGeometry); PrepareBaselineImage(); mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(this->m_DynamicImage); - - for(unsigned int i = 0; i< this->m_DynamicImage->GetTimeSteps(); ++i) { imageTimeSelector->SetTimeNr(i); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkInputImage = imageTimeSelector->GetOutput(); mitk::Image::Pointer outputImage = mitk::Image::New(); outputImage = ConvertSignalToConcentrationCurve(mitkInputImage,this->m_BaselineImage); mitk::ImageReadAccessor accessor(outputImage); tempImage->SetVolume(accessor.GetData(), i); } this->m_ConvertedImage = tempImage; } void mitk::ConcentrationCurveGenerator::PrepareBaselineImage() { mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(this->m_DynamicImage); imageTimeSelector->SetTimeNr(0); imageTimeSelector->UpdateLargestPossibleRegion(); this->m_BaselineImage = imageTimeSelector->GetOutput(); } -mitk::Image::Pointer mitk::ConcentrationCurveGenerator::ConvertSignalToConcentrationCurve(mitk::Image::Pointer inputImage,mitk::Image::Pointer baselineImage) +mitk::Image::Pointer mitk::ConcentrationCurveGenerator::ConvertSignalToConcentrationCurve(const mitk::Image* inputImage, const mitk::Image* baselineImage) { - - - mitk::PixelType m_PixelType = inputImage->GetPixelType(); mitk::Image::Pointer outputImage; if(inputImage->GetPixelType().GetComponentType() != baselineImage->GetPixelType().GetComponentType()) { mitkThrow() << "Input Image and Baseline Image have different Pixel Types. Data not supported"; } if(m_PixelType.GetComponentType() == itk::ImageIOBase::USHORT) { outputImage = convertToConcentration(inputImage, baselineImage); } else if(m_PixelType.GetComponentType() == itk::ImageIOBase::UINT) { outputImage = convertToConcentration(inputImage, baselineImage); } else if(m_PixelType.GetComponentType() == itk::ImageIOBase::INT) { outputImage = convertToConcentration(inputImage, baselineImage); } else if(m_PixelType.GetComponentType() == itk::ImageIOBase::SHORT) { outputImage = convertToConcentration(inputImage, baselineImage); } - /*else if(m_PixelType.GetComponentType() == itk::ImageIOBase::ULONG) - { - outputImage = convertToConcentration(inputImage, baselineImage); - } - else if(m_PixelType.GetComponentType() == itk::ImageIOBase::LONG) - { - outputImage = convertToConcentration(inputImage, baselineImage); - }*/ else if(m_PixelType.GetComponentType() == itk::ImageIOBase::DOUBLE) { outputImage = convertToConcentration(inputImage, baselineImage); } else if(m_PixelType.GetComponentType() == itk::ImageIOBase::FLOAT) { outputImage = convertToConcentration(inputImage, baselineImage); } else { mitkThrow() << "PixelType is "< -mitk::Image::Pointer mitk::ConcentrationCurveGenerator::convertToConcentration(mitk::Image::Pointer inputImage,mitk::Image::Pointer baselineImage) +mitk::Image::Pointer mitk::ConcentrationCurveGenerator::convertToConcentration(const mitk::Image* inputImage, const mitk::Image* baselineImage) { typedef itk::Image InputImageType; typename InputImageType::Pointer itkInputImage = InputImageType::New(); typename InputImageType::Pointer itkBaselineImage = InputImageType::New(); mitk::CastToItkImage(inputImage, itkInputImage ); mitk::CastToItkImage(baselineImage, itkBaselineImage ); mitk::Image::Pointer outputImage; if(this->m_isT2weightedImage) { typedef mitk::ConvertT2ConcentrationFunctor ConversionFunctorT2Type; typedef itk::BinaryFunctorImageFilter FilterT2Type; ConversionFunctorT2Type ConversionT2Functor; ConversionT2Functor.initialize(this->m_T2Factor, this->m_T2EchoTime); typename FilterT2Type::Pointer ConversionT2Filter = FilterT2Type::New(); ConversionT2Filter->SetFunctor(ConversionT2Functor); ConversionT2Filter->SetInput1(itkInputImage); ConversionT2Filter->SetInput2(itkBaselineImage); ConversionT2Filter->Update(); outputImage = mitk::ImportItkImage(ConversionT2Filter->GetOutput())->Clone(); } else { if(this->m_isTurboFlashSequence) { typedef mitk::ConvertToConcentrationTurboFlashFunctor ConversionFunctorTurboFlashType; typedef itk::BinaryFunctorImageFilter FilterTurboFlashType; ConversionFunctorTurboFlashType ConversionTurboFlashFunctor; ConversionTurboFlashFunctor.initialize(this->m_RelaxationTime, this->m_Relaxivity, this->m_RecoveryTime); typename FilterTurboFlashType::Pointer ConversionTurboFlashFilter = FilterTurboFlashType::New(); ConversionTurboFlashFilter->SetFunctor(ConversionTurboFlashFunctor); ConversionTurboFlashFilter->SetInput1(itkInputImage); ConversionTurboFlashFilter->SetInput2(itkBaselineImage); ConversionTurboFlashFilter->Update(); outputImage = mitk::ImportItkImage(ConversionTurboFlashFilter->GetOutput())->Clone(); } else if(this->m_UsingT1Map) { typename InputImageType::Pointer itkT10Image = InputImageType::New(); mitk::CastToItkImage(m_T10Image, itkT10Image); typedef mitk::ConvertToConcentrationViaT1CalcFunctor ConvertToConcentrationViaT1CalcFunctorType; typedef itk::TernaryFunctorImageFilter FilterT1MapType; ConvertToConcentrationViaT1CalcFunctorType ConversionT1MapFunctor; ConversionT1MapFunctor.initialize(this->m_Relaxivity, this->m_RecoveryTime, this->m_FlipAngle); typename FilterT1MapType::Pointer ConversionT1MapFilter = FilterT1MapType::New(); ConversionT1MapFilter->SetFunctor(ConversionT1MapFunctor); ConversionT1MapFilter->SetInput1(itkInputImage); ConversionT1MapFilter->SetInput2(itkBaselineImage); ConversionT1MapFilter->SetInput3(itkT10Image); ConversionT1MapFilter->Update(); outputImage = mitk::ImportItkImage(ConversionT1MapFilter->GetOutput())->Clone(); } else if(this->m_AbsoluteSignalEnhancement) { typedef mitk::ConvertToConcentrationAbsoluteFunctor ConversionFunctorAbsoluteType; typedef itk::BinaryFunctorImageFilter FilterAbsoluteType; ConversionFunctorAbsoluteType ConversionAbsoluteFunctor; ConversionAbsoluteFunctor.initialize(this->m_Factor); typename FilterAbsoluteType::Pointer ConversionAbsoluteFilter = FilterAbsoluteType::New(); ConversionAbsoluteFilter->SetFunctor(ConversionAbsoluteFunctor); ConversionAbsoluteFilter->SetInput1(itkInputImage); ConversionAbsoluteFilter->SetInput2(itkBaselineImage); ConversionAbsoluteFilter->Update(); outputImage = mitk::ImportItkImage(ConversionAbsoluteFilter->GetOutput())->Clone(); } else if(this->m_RelativeSignalEnhancement) { typedef mitk::ConvertToConcentrationRelativeFunctor ConversionFunctorRelativeType; typedef itk::BinaryFunctorImageFilter FilterRelativeType; ConversionFunctorRelativeType ConversionRelativeFunctor; ConversionRelativeFunctor.initialize(this->m_Factor); typename FilterRelativeType::Pointer ConversionRelativeFilter = FilterRelativeType::New(); ConversionRelativeFilter->SetFunctor(ConversionRelativeFunctor); ConversionRelativeFilter->SetInput1(itkInputImage); ConversionRelativeFilter->SetInput2(itkBaselineImage); ConversionRelativeFilter->Update(); outputImage = mitk::ImportItkImage(ConversionRelativeFilter->GetOutput())->Clone(); } } return outputImage; } diff --git a/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/PABeamformingTool.cpp b/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/PABeamformingTool.cpp index d4eac99e9f..b849b6a95e 100644 --- a/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/PABeamformingTool.cpp +++ b/Modules/PhotoacousticsAlgorithms/MitkPABeamformingTool/PABeamformingTool.cpp @@ -1,305 +1,349 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include struct InputParameters { mitk::Image::Pointer inputImage; std::string outputFilename; bool verbose; std::string settingsFile; + std::string imageType; }; struct CropSettings { int above; int below; int right; int left; int zStart; int zEnd; }; struct ResampleSettings { double spacing; + int dimX; }; struct BModeSettings { mitk::PhotoacousticFilterService::BModeMethod method; bool UseLogFilter; }; struct ProcessSettings { bool DoBeamforming; bool DoCropping; bool DoResampling; bool DoBmode; }; InputParameters parseInput(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setCategory("MITK-Photoacoustics"); parser.setTitle("Mitk Photoacoustics Beamforming Tool"); parser.setDescription("Reads a nrrd file as an input and applies a beamforming method as set with the parameters defined in an additionally provided xml file."); parser.setContributor("Computer Assisted Medical Interventions, DKFZ"); parser.setArgumentPrefix("--", "-"); parser.beginGroup("Required parameters"); parser.addArgument( "inputImage", "i", mitkCommandLineParser::Image, "Input image (mitk::Image)", "input image (.nrrd file)", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument( "output", "o", mitkCommandLineParser::File, "Output filename", "output image (.nrrd file)", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.addArgument( "settings", "s", mitkCommandLineParser::String, "settings file", "file containing beamforming and other specifications(.xml file)", us::Any(), false); + parser.addArgument( + "type", "t", mitkCommandLineParser::String, + "image type", "Specifies whether to use the PA or US subsection of the xml file must be 'PA' or 'US'", + us::Any(), false); parser.endGroup(); parser.beginGroup("Optional parameters"); parser.addArgument( "verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose, or rather debug output. (default: false)"); parser.endGroup(); InputParameters input; std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size() == 0) exit(-1); input.verbose = (bool)parsedArgs.count("verbose"); MITK_INFO(input.verbose) << "### VERBOSE OUTPUT ENABLED ###"; if (parsedArgs.count("inputImage")) { MITK_INFO(input.verbose) << "Reading input image..."; input.inputImage = mitk::IOUtil::Load(us::any_cast(parsedArgs["inputImage"])); MITK_INFO(input.verbose) << "Reading input image...[Done]"; } else mitkThrow() << "No input image given."; if (parsedArgs.count("output")) input.outputFilename = us::any_cast(parsedArgs["output"]); else mitkThrow() << "No output image path given.."; if (parsedArgs.count("settings")) input.settingsFile = us::any_cast(parsedArgs["settings"]); else mitkThrow() << "No settings image path given.."; + if (parsedArgs.count("type")) + input.imageType = us::any_cast(parsedArgs["type"]); + else + mitkThrow() << "No settings image type given.."; + return input; } +TiXmlElement* getRootChild(TiXmlElement* root, std::string childName) +{ + for (TiXmlElement* elem = root->FirstChildElement(); elem != NULL; elem = elem->NextSiblingElement()) + { + if (elem->Value() == childName) + return elem; + } + return nullptr; +} + void ParseXML(std::string xmlFile, InputParameters input, mitk::BeamformingSettings::Pointer *bfSet, CropSettings& cropSet, ResampleSettings& resSet, BModeSettings& bmodeSet, ProcessSettings& processSet) { MITK_INFO << "Loading configuration File \"" << xmlFile << "\""; TiXmlDocument doc(xmlFile); if (!doc.LoadFile()) mitkThrow() << "Failed to load settings file \"" << xmlFile << "\" Error: " << doc.ErrorDesc(); TiXmlElement* root = doc.FirstChildElement(); if (root == NULL) { mitkThrow() << "Failed to load file: No root element."; doc.Clear(); } - for (TiXmlElement* elem = root->FirstChildElement(); elem != NULL; elem = elem->NextSiblingElement()) + + TiXmlElement* elemtop = getRootChild(root, input.imageType); + if(elemtop == nullptr) + mitkThrow() << "The xml file is wrongly formatted, no element \"" << input.imageType << "\" found"; + + for (TiXmlElement* elem = elemtop->FirstChildElement(); elem != NULL; elem = elem->NextSiblingElement()) { std::string elemName = elem->Value(); if (elemName == "Beamforming") { + processSet.DoBeamforming = std::stoi(elem->Attribute("do")); + if (!processSet.DoBeamforming) + continue; + float PitchInMeters = std::stof(elem->Attribute("pitchInMeters")); float SpeedOfSound = std::stof(elem->Attribute("speedOfSound")); float Angle = std::stof(elem->Attribute("angle")); bool IsPhotoacousticImage = std::stoi(elem->Attribute("isPhotoacousticImage")); unsigned int SamplesPerLine = std::stoi(elem->Attribute("samplesPerLine")); unsigned int ReconstructionLines = std::stoi(elem->Attribute("reconstructionLines")); float ReconstructionDepth = std::stof(elem->Attribute("reconstructionDepth")); bool UseGPU = std::stoi(elem->Attribute("useGPU")); unsigned int GPUBatchSize = std::stoi(elem->Attribute("GPUBatchSize")); std::string apodizationStr = elem->Attribute("apodization"); mitk::BeamformingSettings::Apodization Apodization = mitk::BeamformingSettings::Apodization::Box; if (apodizationStr == "Box") Apodization = mitk::BeamformingSettings::Apodization::Box; else if (apodizationStr == "Hann") Apodization = mitk::BeamformingSettings::Apodization::Hann; else if (apodizationStr == "Hamm") Apodization = mitk::BeamformingSettings::Apodization::Hamm; else mitkThrow() << "Apodization incorrectly defined in settings"; unsigned int ApodizationArraySize = std::stoi(elem->Attribute("apodizationArraySize")); std::string algorithmStr = elem->Attribute("algorithm"); mitk::BeamformingSettings::BeamformingAlgorithm Algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DAS; if (algorithmStr == "DAS") Algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DAS; else if (algorithmStr == "DMAS") Algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::DMAS; else if (algorithmStr == "sDMAS") Algorithm = mitk::BeamformingSettings::BeamformingAlgorithm::sDMAS; else { mitkThrow() << "Beamforming algorithm incorrectly defined in settings"; } *bfSet = mitk::BeamformingSettings::New( (float)(input.inputImage->GetGeometry()->GetSpacing()[0] / 1000), SpeedOfSound, (float)(input.inputImage->GetGeometry()->GetSpacing()[1] / 1000000), Angle, IsPhotoacousticImage, SamplesPerLine, ReconstructionLines, input.inputImage->GetDimensions(), ReconstructionDepth, UseGPU, GPUBatchSize, mitk::BeamformingSettings::DelayCalc::Spherical, Apodization, ApodizationArraySize, Algorithm ); - processSet.DoBeamforming = std::stoi(elem->Attribute("do")); } - if (elemName == "Cropping") + if (elemName == "Cropping") { + processSet.DoCropping = std::stoi(elem->Attribute("do")); + if (!processSet.DoCropping) + continue; + cropSet.above = std::stoi(elem->Attribute("cutAbove")); cropSet.below = std::stoi(elem->Attribute("cutBelow")); cropSet.right = std::stoi(elem->Attribute("cutRight")); cropSet.left = std::stoi(elem->Attribute("cutLeft")); cropSet.zStart = std::stoi(elem->Attribute("firstSlice")); cropSet.zEnd = std::stoi(elem->Attribute("cutSlices")); - processSet.DoCropping = std::stoi(elem->Attribute("do")); } if (elemName == "Resampling") { - resSet.spacing = std::stod(elem->Attribute("spacing")); processSet.DoResampling = std::stoi(elem->Attribute("do")); + if (!processSet.DoResampling) + continue; + + resSet.spacing = std::stod(elem->Attribute("spacing")); + resSet.dimX = std::stoi(elem->Attribute("dimX")); } if (elemName == "BMode") { + processSet.DoBmode = std::stoi(elem->Attribute("do")); + if (!processSet.DoBmode) + continue; + std::string methodStr = elem->Attribute("method"); if (methodStr == "EnvelopeDetection") bmodeSet.method = mitk::PhotoacousticFilterService::BModeMethod::EnvelopeDetection; else if (elem->Attribute("method") == "Abs") bmodeSet.method = mitk::PhotoacousticFilterService::BModeMethod::Abs; else mitkThrow() << "BMode method incorrectly set in configuration file"; bmodeSet.UseLogFilter = (bool)std::stoi(elem->Attribute("useLogFilter")); - processSet.DoBmode = std::stoi(elem->Attribute("do")); } } } int main(int argc, char * argv[]) { auto input = parseInput(argc, argv); mitk::BeamformingSettings::Pointer bfSettings; BModeSettings bmodeSettings{ mitk::PhotoacousticFilterService::BModeMethod::EnvelopeDetection, false }; CropSettings cropSettings{ 0,0,0,0,0,0 }; ResampleSettings resSettings{ 0.15 }; ProcessSettings processSettings{ true, false, false }; MITK_INFO << "Parsing settings XML..."; try { ParseXML(input.settingsFile, input, &bfSettings, cropSettings, resSettings, bmodeSettings, processSettings); } catch (mitk::Exception e) { MITK_INFO << e; return -1; } MITK_INFO << "Parsing settings XML...[Done]"; MITK_INFO(input.verbose) << "Beamforming input image..."; mitk::Image::Pointer inputImage = input.inputImage; if (!(inputImage->GetPixelType().GetTypeAsString() == "scalar (float)" || inputImage->GetPixelType().GetTypeAsString() == " (float)")) { // we need a float image, so cast it here MITK_INFO(input.verbose) << "Casting input image to float..."; mitk::CastToFloatImageFilter::Pointer castFilter = mitk::CastToFloatImageFilter::New(); castFilter->SetInput(inputImage); castFilter->Update(); inputImage = castFilter->GetOutput(); MITK_INFO << inputImage->GetPixelType().GetPixelTypeAsString(); MITK_INFO(input.verbose) << "Casting input image to float...[Done]"; } mitk::PhotoacousticFilterService::Pointer m_FilterService = mitk::PhotoacousticFilterService::New(); mitk::Image::Pointer output = inputImage; if (processSettings.DoBeamforming) { MITK_INFO(input.verbose) << "Beamforming input image..."; output = m_FilterService->ApplyBeamforming(output, bfSettings); MITK_INFO(input.verbose) << "Beamforming input image...[Done]"; } if (processSettings.DoCropping) { int err; MITK_INFO(input.verbose) << "Applying Crop filter to image..."; output = m_FilterService->ApplyCropping(output, cropSettings.above, cropSettings.below, cropSettings.right, cropSettings.left, cropSettings.zStart, cropSettings.zEnd, &err); MITK_INFO(input.verbose) << "Applying Crop filter to image...[Done]"; } if (processSettings.DoResampling) { - double spacing[3] = {output->GetGeometry()->GetSpacing()[0], resSettings.spacing, output->GetGeometry()->GetSpacing()[2]}; + double spacing[3] = {output->GetGeometry()->GetSpacing()[0]*output->GetDimension(0)/resSettings.dimX, resSettings.spacing, output->GetGeometry()->GetSpacing()[2]}; MITK_INFO(input.verbose) << "Applying Resample filter to image..."; output = m_FilterService->ApplyResampling(output, spacing); + if (output->GetDimension(0) != resSettings.dimX) + { + double dim[3] = {(double)resSettings.dimX, (double)output->GetDimension(1), (double)output->GetDimension(2)}; + output = m_FilterService->ApplyResamplingToDim(output, dim); + } MITK_INFO(input.verbose) << "Applying Resample filter to image...[Done]"; } if (processSettings.DoBmode) { MITK_INFO(input.verbose) << "Applying BModeFilter to image..."; output = m_FilterService->ApplyBmodeFilter(output, bmodeSettings.method, bmodeSettings.UseLogFilter); MITK_INFO(input.verbose) << "Applying BModeFilter to image...[Done]"; } MITK_INFO(input.verbose) << "Saving image..."; mitk::IOUtil::Save(output, input.outputFilename); MITK_INFO(input.verbose) << "Saving image...[Done]"; MITK_INFO(input.verbose) << "Beamforming input image...[Done]"; } diff --git a/Modules/PhotoacousticsLib/MitkSpectralUnmixing/SpectralUnmixingApp.cpp b/Modules/PhotoacousticsLib/MitkSpectralUnmixing/SpectralUnmixingApp.cpp index 7818bbd18b..2a3ad625ea 100644 --- a/Modules/PhotoacousticsLib/MitkSpectralUnmixing/SpectralUnmixingApp.cpp +++ b/Modules/PhotoacousticsLib/MitkSpectralUnmixing/SpectralUnmixingApp.cpp @@ -1,289 +1,315 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "mitkPALinearSpectralUnmixingFilter.h" #include "mitkPASpectralUnmixingFilterBase.h" #include "mitkPASpectralUnmixingFilterVigra.h" #include "mitkPASpectralUnmixingSO2.h" #include #include #include #include #include #include "mitkPreferenceListReaderOptionsFunctor.h" /* \brief The spectral unmixing mini app (SUMA) is designed to enable batch processing for spectral unmixing. For detailed documentation look into the header files of the included spectral unmixing filters.*/ struct InputParameters { std::string inputFilename; std::string outputFileStruct; // "E:/mydata/awesome_exp_unmixed/a" will be saved as "a_HbO2_SU_.nrrd", "a_Hb_SU_.nrrd" and "a_sO2_.nrrd"; std::string inputAlg; + std::string outputFileNumber; mitkCommandLineParser::StringContainerType inputWavelengths; mitkCommandLineParser::StringContainerType inputWeights; }; InputParameters parseInput(int argc, char *argv[]) { - MITK_INFO << "Parsing arguments..."; + //MITK_INFO << "Parsing arguments..."; mitkCommandLineParser parser; parser.setCategory("MITK-Photoacoustics"); parser.setTitle("Mitk Spectral Unmixing App"); parser.setDescription("Batch processing for spectral unmixing."); parser.setContributor("Computer Assisted Medical Interventions, DKFZ"); parser.setArgumentPrefix("--", "-"); parser.beginGroup("Required parameters"); parser.addArgument("inputFilename", "i", mitkCommandLineParser::Directory, "Input Filename (NAME.nrrd)", "input filename", us::Any(), - false, false, false, mitkCommandLineParser::Input); + false); parser.addArgument("outputFileStruct", "o", mitkCommandLineParser::Directory, - "Input save name (name without ending!)", - "input save name", + "Output save name (name without ending!)", + "Output save name", + us::Any(), + false); + parser.addArgument("outputFileNumber", + "n", + mitkCommandLineParser::String, + "Output file number", + "Output save number", us::Any(), - false, false, false, mitkCommandLineParser::Output); + false); parser.addArgument("inputWavelengths", "l", mitkCommandLineParser::StringList, "Input wavelengths (123 124 125 ... int blank int blank)", "input wavelengths", us::Any(), false); parser.addArgument("inputAlg", "a", mitkCommandLineParser::String, "Input algorithm (string)", "input algorithm", us::Any(), false); parser.addArgument("inputWeights", "w", mitkCommandLineParser::StringList, "Input weights (123 124 125 ... int in % blank int in % blank)", "input weights", us::Any(), true); parser.endGroup(); InputParameters input; std::map parsedArgs = parser.parseArguments(argc, argv); if (argc == 0) exit(-1); - for (int i = 0; i < argc; ++i) - { - MITK_INFO << argv[i]; - } + //for (int i = 0; i < argc; ++i) + //{ + // MITK_INFO << argv[i]; + //} + if (parsedArgs.count("inputFilename")) { input.inputFilename = us::any_cast(parsedArgs["inputFilename"]); } else { MITK_ERROR << "Error: No input file"; mitkThrow() << "Error: No input file"; } if (parsedArgs.count("outputFileStruct")) { input.outputFileStruct = us::any_cast(parsedArgs["outputFileStruct"]); } else { MITK_ERROR << "Error: No output"; mitkThrow() << "Error: No output"; } + if (parsedArgs.count("outputFileNumber")) + { + input.outputFileNumber = us::any_cast(parsedArgs["outputFileNumber"]); + } + else + { + MITK_ERROR << "Error: No output number"; + mitkThrow() << "Error: No output number"; + } + if (parsedArgs.count("inputWavelengths")) { input.inputWavelengths = us::any_cast(parsedArgs["inputWavelengths"]); } else { MITK_ERROR << "Error: No wavelengths"; mitkThrow() << "Error: No wavelengths"; } if (parsedArgs.count("inputAlg")) { input.inputAlg = us::any_cast(parsedArgs["inputAlg"]); } else { MITK_ERROR << "Error: No algorithm"; mitkThrow() << "Error: No algorithm"; } if (parsedArgs.count("inputWeights")) { input.inputWeights = us::any_cast(parsedArgs["inputWeights"]); } - MITK_INFO << "Parsing arguments...[Done]"; + //MITK_INFO << "Parsing arguments...[Done]"; return input; } // Class takes string and sets algorithm for spectral unmixing in the corresponding filter class mitk::pa::SpectralUnmixingFilterBase::Pointer GetFilterInstance(std::string algorithm, std::vector weights = std::vector()) { mitk::pa::SpectralUnmixingFilterBase::Pointer spectralUnmixingFilter; if (algorithm == "QR") { spectralUnmixingFilter = mitk::pa::LinearSpectralUnmixingFilter::New(); dynamic_cast(spectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::LinearSpectralUnmixingFilter::AlgortihmType::HOUSEHOLDERQR); } else if (algorithm == "SVD") { spectralUnmixingFilter = mitk::pa::LinearSpectralUnmixingFilter::New(); dynamic_cast(spectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::LinearSpectralUnmixingFilter::AlgortihmType::JACOBISVD); } else if (algorithm == "LU") { spectralUnmixingFilter = mitk::pa::LinearSpectralUnmixingFilter::New(); dynamic_cast(spectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::LinearSpectralUnmixingFilter::AlgortihmType::FULLPIVLU); } else if (algorithm == "NNLS") { spectralUnmixingFilter = mitk::pa::SpectralUnmixingFilterVigra::New(); dynamic_cast(spectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::LARS); } else if (algorithm == "WLS") { spectralUnmixingFilter = mitk::pa::SpectralUnmixingFilterVigra::New(); dynamic_cast(spectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::WEIGHTED); std::vector weightVec = weights; for (unsigned int i = 0; i < weightVec.size(); ++i) { dynamic_cast(spectralUnmixingFilter.GetPointer()) ->AddWeight(weightVec[i]); } } return spectralUnmixingFilter; } int main(int argc, char *argv[]) { auto input = parseInput(argc, argv); std::string algo = input.inputAlg; std::string outputDir = input.outputFileStruct; + std::string outputNumber = input.outputFileNumber; + auto inputWls = input.inputWavelengths; std::vector wavelengths; for (unsigned int s = 0; s < inputWls.size(); ++s) { int wl = std::stoi(inputWls[s]); wavelengths.push_back(wl); - MITK_INFO << "Wavelength: " << wl << "\n"; + //MITK_INFO << "Wavelength: " << wl << "\n"; } mitk::pa::SpectralUnmixingFilterBase::Pointer m_SpectralUnmixingFilter; if (algo == "WLS") { auto inputW = input.inputWeights; std::vector Weights; for (unsigned int s = 0; s < inputW.size(); ++s) { int w = std::stoi(inputW[s]); Weights.push_back(w); - MITK_INFO << "Weights: " << w << "\n"; + //MITK_INFO << "Weights: " << w << "\n"; } m_SpectralUnmixingFilter = GetFilterInstance(algo, Weights); } else { m_SpectralUnmixingFilter = GetFilterInstance(algo); } m_SpectralUnmixingFilter->Verbose(false); m_SpectralUnmixingFilter->RelativeError(false); m_SpectralUnmixingFilter->AddChromophore(mitk::pa::PropertyCalculator::ChromophoreType::OXYGENATED); m_SpectralUnmixingFilter->AddChromophore(mitk::pa::PropertyCalculator::ChromophoreType::DEOXYGENATED); m_SpectralUnmixingFilter->AddOutputs(2); for (unsigned int wIdx = 0; wIdx < wavelengths.size(); ++wIdx) { m_SpectralUnmixingFilter->AddWavelength(wavelengths[wIdx]); - MITK_INFO << wavelengths[wIdx]; + //MITK_INFO << wavelengths[wIdx]; } //to add a batch processing: loop for a dir start here; don't forget to set a counter to the three output savenames!!! std::string inputImage = input.inputFilename; auto m_inputImage = mitk::IOUtil::Load(inputImage); m_SpectralUnmixingFilter->SetInput(m_inputImage); m_SpectralUnmixingFilter->Update(); auto output1 = m_SpectralUnmixingFilter->GetOutput(0); auto output2 = m_SpectralUnmixingFilter->GetOutput(1); output1->SetSpacing(m_inputImage->GetGeometry()->GetSpacing()); output2->SetSpacing(m_inputImage->GetGeometry()->GetSpacing()); - std::string unmixingOutputHbO2 = outputDir + "_HbO2_SU_.nrrd"; - std::string unmixingOutputHb = outputDir + "_Hb_SU_.nrrd"; - mitk::IOUtil::Save(output1, unmixingOutputHbO2); - mitk::IOUtil::Save(output2, unmixingOutputHb); + std::string unmixingOutputHbO2 = outputDir + "HbO2." + outputNumber + ".nrrd"; + std::string unmixingOutputHb = outputDir + "Hb." + outputNumber + ".nrrd"; + //mitk::IOUtil::Save(output1, unmixingOutputHbO2); + //mitk::IOUtil::Save(output2, unmixingOutputHb); auto m_sO2 = mitk::pa::SpectralUnmixingSO2::New(); m_sO2->Verbose(false); m_sO2->SetInput(0, output1); m_sO2->SetInput(1, output2); m_sO2->Update(); mitk::Image::Pointer sO2 = m_sO2->GetOutput(0); + mitk::Image::Pointer tHb = m_sO2->GetOutput(1); sO2->SetSpacing(m_inputImage->GetGeometry()->GetSpacing()); + tHb->SetSpacing(m_inputImage->GetGeometry()->GetSpacing()); - std::string outputSo2 = outputDir + "_sO2_.nrrd"; + std::string outputSo2 = outputDir + "sO2." + outputNumber + ".nrrd"; mitk::IOUtil::Save(sO2, outputSo2); + std::string outputTHb = outputDir + "tHb." + outputNumber + ".nrrd"; + mitk::IOUtil::Save(tHb, outputTHb); + m_sO2 = nullptr; m_SpectralUnmixingFilter = nullptr; //to add a batch processing: loop for a dir end here MITK_INFO << "Spectral Unmixing DONE"; } diff --git a/Modules/PhotoacousticsLib/include/mitkPASpectralUnmixingSO2.h b/Modules/PhotoacousticsLib/include/mitkPASpectralUnmixingSO2.h index c822e102f7..63bd582815 100644 --- a/Modules/PhotoacousticsLib/include/mitkPASpectralUnmixingSO2.h +++ b/Modules/PhotoacousticsLib/include/mitkPASpectralUnmixingSO2.h @@ -1,106 +1,116 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MITKPHOTOACOUSTICSPECTRALUNMIXINGSO2_H #define MITKPHOTOACOUSTICSPECTRALUNMIXINGSO2_H #include "mitkImageToImageFilter.h" #include //Includes for smart pointer usage #include "mitkCommon.h" #include "itkLightObject.h" namespace mitk { namespace pa { /** * \brief derives out of two identical sized MITK images the oxygen saturation and return one MITK image as result. Furthermore * it is possible to set settings that the result shows just SO2 values above a threshold, or above a input value for Hb, HbO2 to * get just a oxygen saturation image of interessting structures. * * Input: * The input has to be two 3D MITK images. The order of the inputs matter! The first input has to be the Hb image the second input * has to be the HbO2 image. The settings are integer values. The SO2 threshold therefore is percentage value. * * Output: * The output will be one MITK image. Where one can see the oxygen saturation of all pixels above the set threholds. If a pixel is * below a threhold or NAN then the value will be set to zero. + * + * UPDATE: SO2 Filter will get an second output, the total hemoglobin value with ->GetOutput(1). */ class MITKPHOTOACOUSTICSLIB_EXPORT SpectralUnmixingSO2 : public mitk::ImageToImageFilter { public: mitkClassMacro(SpectralUnmixingSO2, mitk::ImageToImageFilter); itkFactorylessNewMacro(Self); /** * \brief AddSO2Settings takes integers and writes them at the end of the m_SO2Settings vector. * @param value of the Setting */ virtual void AddSO2Settings(int value); /** * \brief Verbose gives more information to the console. Default value is false. * @param m_Verbose is the boolian to activate the MITK_INFO logged to the console */ virtual void Verbose(bool verbose); protected: /** * \brief Constructor sets number of input images to two and number of output images to one, respectively. */ SpectralUnmixingSO2(); virtual ~SpectralUnmixingSO2(); std::vector m_SO2Settings; bool m_Verbose = false; private: /** * \brief Inherit from the "ImageToImageFilter" Superclass. Herain it calls InitializeOutputs and the CheckPreConditions * methods and enables pixelwise access to the inputs to calculate the oxygen saturation via the "calculate SO2" method. */ virtual void GenerateData() override; /** * \brief Initialized output images with the same size like the input image. The pixel type is set to float. */ virtual void InitializeOutputs(); /** * \brief Checks if the dimensions of the input images are equal and made out of floating poinhts. * @throws if the inputs don't have the same size * @throws if input images don't consist of floats */ virtual void CheckPreConditions(mitk::Image::Pointer inputHbO2, mitk::Image::Pointer inputHb); /** * \brief calculates HbO2 / (Hb + HbO2) and afterwards checks if the result is significant (SO2ValueNotSiginificant method). * If not the method returns zero otherwise it returns the calculated result. * @param pixelHb is the pixel value of the Hb input. * @param pixelHb is the pixel value of the Hb input. - * @warn if the HbO2 value is NAN (in patricular if Hb == -HbO2), but result will be set to zero + * @warn if the sO2 value is NAN (in patricular if Hb == -HbO2), but result will be set to zero */ float CalculateSO2(float pixelHb, float pixelHbO2); + /** + * \brief calculates (Hb + HbO2). + * @param pixelHb is the pixel value of the Hb input. + * @param pixelHb is the pixel value of the Hb input. + * @warn if the tHb value is NAN (in patricular if Hb == -HbO2), but result will be set to zero + */ + float CalculateTHb(float pixelHb, float pixelHbO2); + /** * \brief return true if SO2 result is not significant by checking if the input values are above the threshold of the settings */ bool SO2ValueNotSiginificant(float Hb, float HbO2, float result); }; } } #endif // MITKPHOTOACOUSTICSPECTRALUNMIXINGSO2_ diff --git a/Modules/PhotoacousticsLib/src/SUFilter/mitkPASpectralUnmixingSO2.cpp b/Modules/PhotoacousticsLib/src/SUFilter/mitkPASpectralUnmixingSO2.cpp index 0c9bc04bac..85fed87b2b 100644 --- a/Modules/PhotoacousticsLib/src/SUFilter/mitkPASpectralUnmixingSO2.cpp +++ b/Modules/PhotoacousticsLib/src/SUFilter/mitkPASpectralUnmixingSO2.cpp @@ -1,165 +1,189 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPASpectralUnmixingSO2.h" // ImageAccessor #include #include mitk::pa::SpectralUnmixingSO2::SpectralUnmixingSO2() { this->SetNumberOfIndexedInputs(2); - this->SetNumberOfIndexedOutputs(1); + this->SetNumberOfIndexedOutputs(2); this->SetNthOutput(0, mitk::Image::New()); + this->SetNthOutput(1, mitk::Image::New()); + } mitk::pa::SpectralUnmixingSO2::~SpectralUnmixingSO2() { } void mitk::pa::SpectralUnmixingSO2::Verbose(bool verbose) { m_Verbose = verbose; } void mitk::pa::SpectralUnmixingSO2::GenerateData() { MITK_INFO(m_Verbose) << "GENERATING DATA.."; // Get input image mitk::Image::Pointer inputHbO2 = GetInput(0); mitk::Image::Pointer inputHb = GetInput(1); CheckPreConditions(inputHbO2, inputHb); unsigned int xDim = inputHbO2->GetDimensions()[0]; unsigned int yDim = inputHbO2->GetDimensions()[1]; unsigned int zDim = inputHbO2->GetDimensions()[2]; InitializeOutputs(); mitk::ImageReadAccessor readAccessHbO2(inputHbO2); mitk::ImageReadAccessor readAccessHb(inputHb); const float* inputDataArrayHbO2 = ((const float*)readAccessHbO2.GetData()); const float* inputDataArrayHb = ((const float*)readAccessHb.GetData()); auto output = GetOutput(0); + auto output1 = GetOutput(1); + mitk::ImageWriteAccessor writeOutput(output); float* writeBuffer = (float *)writeOutput.GetData(); + mitk::ImageWriteAccessor writeOutput1(output1); + float* writeBuffer1 = (float *)writeOutput1.GetData(); + for (unsigned int x = 0; x < xDim; x++) { for (unsigned int y = 0; y < yDim; y++) { for (unsigned int z = 0;z < zDim; z++) { unsigned int pixelNumber = (xDim*yDim * z) + x * yDim + y; float pixelHb = inputDataArrayHb[pixelNumber]; float pixelHbO2 = inputDataArrayHbO2[pixelNumber]; float resultSO2 = CalculateSO2(pixelHb, pixelHbO2); writeBuffer[(xDim*yDim * z) + x * yDim + y] = resultSO2; + float resultTHb = CalculateTHb(pixelHb, pixelHbO2); + writeBuffer1[(xDim*yDim * z) + x * yDim + y] = resultTHb; } } } MITK_INFO(m_Verbose) << "GENERATING DATA...[DONE]"; } void mitk::pa::SpectralUnmixingSO2::CheckPreConditions(mitk::Image::Pointer inputHbO2, mitk::Image::Pointer inputHb) { unsigned int xDimHb = inputHb->GetDimensions()[0]; unsigned int yDimHb = inputHb->GetDimensions()[1]; unsigned int zDimHb = inputHb->GetDimensions()[2]; unsigned int xDimHbO2 = inputHbO2->GetDimensions()[0]; unsigned int yDimHbO2 = inputHbO2->GetDimensions()[1]; unsigned int zDimHbO2 = inputHbO2->GetDimensions()[2]; if (xDimHb != xDimHbO2 || yDimHb != yDimHbO2 || zDimHb != zDimHbO2) mitkThrow() << "DIMENTIONALITY ERROR!"; if (inputHbO2->GetPixelType() != mitk::MakeScalarPixelType()) mitkThrow() << "PIXELTYPE ERROR! FLOAT REQUIRED"; if (inputHb->GetPixelType() != mitk::MakeScalarPixelType()) mitkThrow() << "PIXELTYPE ERROR! FLOAT REQUIRED"; MITK_INFO(m_Verbose) << "CHECK PRECONDITIONS ...[DONE]"; } void mitk::pa::SpectralUnmixingSO2::InitializeOutputs() { // UNUSED unsigned int numberOfInputs = GetNumberOfIndexedInputs(); unsigned int numberOfOutputs = GetNumberOfIndexedOutputs(); mitk::PixelType pixelType = mitk::MakeScalarPixelType(); const int NUMBER_OF_SPATIAL_DIMENSIONS = 3; auto* dimensions = new unsigned int[NUMBER_OF_SPATIAL_DIMENSIONS]; for(unsigned int dimIdx=0; dimIdxGetDimensions()[dimIdx]; } for (unsigned int outputIdx = 0; outputIdx < numberOfOutputs; outputIdx++) { GetOutput(outputIdx)->Initialize(pixelType, NUMBER_OF_SPATIAL_DIMENSIONS, dimensions); } } float mitk::pa::SpectralUnmixingSO2::CalculateSO2(float Hb, float HbO2) { float result = HbO2 / (Hb + HbO2); if (result != result) { MITK_WARN(m_Verbose) << "SO2 VALUE NAN! WILL BE SET TO ZERO!"; return 0; } else { if (SO2ValueNotSiginificant(Hb, HbO2, result)) { return 0; } else return result; } } +float mitk::pa::SpectralUnmixingSO2::CalculateTHb(float Hb, float HbO2) +{ + float result = (Hb + HbO2); + + if (result != result) + { + MITK_WARN(m_Verbose) << "SO2 VALUE NAN! WILL BE SET TO ZERO!"; + return 0; + } + else + { + return result; + } +} + void mitk::pa::SpectralUnmixingSO2::AddSO2Settings(int value) { m_SO2Settings.push_back(value); } bool mitk::pa::SpectralUnmixingSO2::SO2ValueNotSiginificant(float Hb, float HbO2, float result) { std::vector significant; significant.push_back(HbO2); significant.push_back(Hb); significant.push_back(HbO2 + Hb); significant.push_back(100*(result)); for (unsigned int i = 0; i < m_SO2Settings.size(); ++i) { if (m_SO2Settings[i] != 0 && m_SO2Settings[i] > significant[i] && (std::abs(m_SO2Settings[i] - significant[i]) > 1e-7)) { return true; } } return false; } diff --git a/Modules/QtPython/Testing/files.cmake b/Modules/QtPython/Testing/files.cmake index 4acaf49de5..d4301946da 100644 --- a/Modules/QtPython/Testing/files.cmake +++ b/Modules/QtPython/Testing/files.cmake @@ -1,13 +1,3 @@ set(MODULE_TESTS mitkPythonTest.cpp ) - -#TODO: temporarily disabled untill segfault is fixed (bug-19152) -if(UNIX) - set(MODULE_TESTS ${MODULE_TESTS} mitkVtkPythonTest.cpp) -endif() - -if(MITK_USE_OpenCV) - set(MODULE_TESTS ${MODULE_TESTS} mitkCvPythonTest.cpp) -endif() - diff --git a/Modules/QtPython/Testing/mitkCvPythonTest.cpp b/Modules/QtPython/Testing/mitkCvPythonTest.cpp deleted file mode 100644 index 8125553574..0000000000 --- a/Modules/QtPython/Testing/mitkCvPythonTest.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class mitkCvPythonTestSuite : public mitk::TestFixture -{ - CPPUNIT_TEST_SUITE(mitkCvPythonTestSuite); - MITK_TEST(testCVImageTransfer); - MITK_TEST(testOpenCVMedianFilter); - CPPUNIT_TEST_SUITE_END(); - -public: - - mitk::Image::Pointer m_Image2D; - - mitk::IPythonService* m_PythonService; - void setUp() override - { - us::ModuleContext* context = us::GetModuleContext(); - us::ServiceReference m_PythonServiceRef = context->GetServiceReference(); - m_PythonService = dynamic_cast ( context->GetService(m_PythonServiceRef) ); - mitk::IPythonService::ForceLoadModule(); - - m_Image2D = mitk::IOUtil::Load(GetTestDataFilePath("Png2D-bw.png")); - } - - void testCVImageTransfer() - { - std::string varName("mitkImage"); - CPPUNIT_ASSERT_MESSAGE ( "Is OpenCV Python Wrapping available?", - m_PythonService->IsOpenCvPythonWrappingAvailable() == true ); - - CPPUNIT_ASSERT_MESSAGE( "Valid image copied to python import should return true.", - m_PythonService->CopyToPythonAsCvImage( m_Image2D, varName) == true ); - - mitk::Image::Pointer pythonImage = m_PythonService->CopyCvImageFromPython(varName); - - // todo pixeltypes do not match, cv is changing it - //CPPUNIT_ASSERT_MESSAGE( "Compare if images are equal after transfer.", - // mitk::Equal(pythonImage,m_Image2D) ); - } - - //TODO opencv median filter, add cpp test code - void testOpenCVMedianFilter() - { - std::string code = "mitkImage_new = cv2.medianBlur(mitkImage, 3)"; - // simple itk median filter in python - CPPUNIT_ASSERT_MESSAGE ( "Is OpenCV Python Wrapping available?", m_PythonService->IsOpenCvPythonWrappingAvailable() == true ); - - CPPUNIT_ASSERT_MESSAGE( "Valid image copied to python import should return true.", m_PythonService->CopyToPythonAsCvImage(m_Image2D, "mitkImage") == true ); - - m_PythonService->Execute( code, mitk::IPythonService::MULTI_LINE_COMMAND ); - - CPPUNIT_ASSERT_MESSAGE( "Python execute error occured.", !m_PythonService->PythonErrorOccured()); - } -}; - -MITK_TEST_SUITE_REGISTRATION(mitkCvPython) diff --git a/Modules/QtPython/Testing/mitkVtkPythonTest.cpp b/Modules/QtPython/Testing/mitkVtkPythonTest.cpp deleted file mode 100644 index 48d1f8a049..0000000000 --- a/Modules/QtPython/Testing/mitkVtkPythonTest.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class mitkVtkPythonTestSuite : public mitk::TestFixture -{ - CPPUNIT_TEST_SUITE(mitkVtkPythonTestSuite); - MITK_TEST(testSurfaceTransfer); -// MITK_TEST(testVtkCreateConePythonSnippet); - //MITK_TEST(testVtkDecimateProPythonSnippet); - CPPUNIT_TEST_SUITE_END(); - -public: - - mitk::IPythonService* m_PythonService; - mitk::Surface::Pointer m_Surface; - - void setUp() override - { - us::ModuleContext* context = us::GetModuleContext(); - us::ServiceReference m_PythonServiceRef = context->GetServiceReference(); - m_PythonService = dynamic_cast ( context->GetService(m_PythonServiceRef) ); - mitk::IPythonService::ForceLoadModule(); - - m_Surface = mitk::IOUtil::Load(GetTestDataFilePath("binary.stl")); - } - - void testSurfaceTransfer() - { - std::string varName("mitkSurface"); - CPPUNIT_ASSERT_MESSAGE ( "Is VTK Python Wrapping available?", m_PythonService->IsVtkPythonWrappingAvailable() == true ); - - CPPUNIT_ASSERT_MESSAGE( "Valid surface copied to python import should return true.", - m_PythonService->CopyToPythonAsVtkPolyData( m_Surface, varName) == true ); - - mitk::Surface::Pointer pythonSurface = m_PythonService->CopyVtkPolyDataFromPython(varName); - - CPPUNIT_ASSERT_MESSAGE( "Compare if surfaces are equal after transfer.", mitk::Equal(*pythonSurface.GetPointer(),*m_Surface.GetPointer(),mitk::eps,true) ); - } - - void testVtkCreateConePythonSnippet() - { - std::string code = - "import vtk\n" - "coneSrc = vtk.vtkConeSource()\n" - "coneSrc.SetResolution(60)\n" - "coneSrc.SetCenter(-2,0,0)\n" - "coneSrc.Update()\n" - "cone = coneSrc.GetOutput()"; - // cone in cpp -// mitk::Surface::Pointer mitkSurface = mitk::Surface::New(); -// vtkSmartPointer coneSrc = vtkSmartPointer::New(); -// coneSrc->SetResolution(60); -// coneSrc->SetCenter(-2,0,0); -// coneSrc->Update(); -// mitkSurface->SetVtkPolyData(coneSrc->GetOutput()); - - // run python code - CPPUNIT_ASSERT_MESSAGE ( "Is VTK Python Wrapping available?", m_PythonService->IsVtkPythonWrappingAvailable() == true ); - -// m_PythonService->Execute( code, mitk::IPythonService::MULTI_LINE_COMMAND ); -// CPPUNIT_ASSERT_MESSAGE( "Python execute error occured.", !m_PythonService->PythonErrorOccured()); - -// mitk::Surface::Pointer pythonSurface = m_PythonService->CopyVtkPolyDataFromPython("cone"); - -// CPPUNIT_ASSERT_MESSAGE( "Compare if cones are equal.", mitk::Equal(*pythonSurface.GetPointer(), *mitkSurface.GetPointer(), mitk::eps,true) ); - } - - void testVtkDecimateProPythonSnippet() - { - std::string code = - "import vtk\n" - "deci = vtk.vtkDecimatePro()\n" - "deci.SetInputData( mitkSurface )\n" - "deci.SetTargetReduction(0.9)\n" - "deci.PreserveTopologyOn()\n" - "deci.Update()\n" - "mitkSurface_new = deci.GetOutput()\n"; - - // decimate pro in cpp - mitk::Surface::Pointer mitkSurface = mitk::Surface::New(); - vtkSmartPointer deci = vtkSmartPointer::New(); - deci->SetInputData(m_Surface->GetVtkPolyData()); - deci->SetTargetReduction(0.9); - deci->PreserveTopologyOn(); - deci->Update(); - mitkSurface->SetVtkPolyData(deci->GetOutput()); - - // decimate pro in python - CPPUNIT_ASSERT_MESSAGE ( "Is VTK Python Wrapping available?", m_PythonService->IsVtkPythonWrappingAvailable() == true ); - - CPPUNIT_ASSERT_MESSAGE( "Valid surface copied to python import should return true.", m_PythonService->CopyToPythonAsVtkPolyData( m_Surface, "mitkSurface") == true ); - - m_PythonService->Execute( code, mitk::IPythonService::MULTI_LINE_COMMAND ); - CPPUNIT_ASSERT_MESSAGE( "Python execute error occured.", !m_PythonService->PythonErrorOccured()); - - mitk::Surface::Pointer pythonSurface = m_PythonService->CopyVtkPolyDataFromPython("mitkSurface_new"); - - CPPUNIT_ASSERT_MESSAGE( "Compare if surfaces are equal.", mitk::Equal(*pythonSurface.GetPointer(), *mitkSurface.GetPointer(), mitk::eps,true) ); - } -}; - -MITK_TEST_SUITE_REGISTRATION(mitkVtkPython) diff --git a/Modules/QtWidgets/include/QmitkDataStorageTreeModelInternalItem.h b/Modules/QtWidgets/include/QmitkDataStorageTreeModelInternalItem.h index e61d5c8819..0fa945b1ea 100644 --- a/Modules/QtWidgets/include/QmitkDataStorageTreeModelInternalItem.h +++ b/Modules/QtWidgets/include/QmitkDataStorageTreeModelInternalItem.h @@ -1,101 +1,101 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKDATASTORAGETREEMODELINTERNALITEM_H_ #define QMITKDATASTORAGETREEMODELINTERNALITEM_H_ #include #include #include #include /// /// Helper class to represent a tree structure of DataNodes /// class MITKQTWIDGETS_EXPORT QmitkDataStorageTreeModelInternalItem { public: /// /// Constructs a new QmitkDataStorageTreeModelInternalItem with the given DataNode (must not be 0) /// QmitkDataStorageTreeModelInternalItem(mitk::DataNode *_DataNode, QmitkDataStorageTreeModelInternalItem *_Parent = 0); /// /// Removes itself as child from its parent-> Does not delete its children /// \sa Delete() /// virtual ~QmitkDataStorageTreeModelInternalItem(); /// /// Find the index of an item /// int IndexOfChild(const QmitkDataStorageTreeModelInternalItem *item) const; /// /// \return The child at pos index or 0 if it not exists /// QmitkDataStorageTreeModelInternalItem *GetChild(int index) const; /// /// Find the QmitkDataStorageTreeModelInternalItem containing a special tree node (recursive tree function) /// QmitkDataStorageTreeModelInternalItem *Find(const mitk::DataNode *_DataNode) const; /// /// Get the amount of children /// int GetChildCount() const; /// /// \return the index of this node in its parent list /// int GetIndex() const; /// /// \return the parent of this tree item /// QmitkDataStorageTreeModelInternalItem *GetParent() const; /// /// Return the DataNode associated with this node /// - mitk::DataNode* GetDataNode() const; + mitk::DataNode::Pointer GetDataNode() const; /// /// Get all children as vector /// std::vector GetChildren() const; /// /// add another item as a child of this (only if not already in that list) /// void AddChild(QmitkDataStorageTreeModelInternalItem *item); /// /// remove another item as child from this /// void RemoveChild(QmitkDataStorageTreeModelInternalItem *item); /// /// inserts a child at the given position. if pos is not in range /// the element is added at the end /// void InsertChild(QmitkDataStorageTreeModelInternalItem *item, int index = -1); /// Sets the parent on the QmitkDataStorageTreeModelInternalItem void SetParent(QmitkDataStorageTreeModelInternalItem *_Parent); /// /// Deletes the whole tree branch /// void Delete(); protected: QmitkDataStorageTreeModelInternalItem *m_Parent; std::vector m_Children; - mitk::DataNode::Pointer m_DataNode; + mitk::WeakPointer m_DataNode; }; #endif /* QMITKDATASTORAGETREEMODEL_H_ */ diff --git a/Modules/QtWidgets/include/QmitkLevelWindowWidgetContextMenu.h b/Modules/QtWidgets/include/QmitkLevelWindowWidgetContextMenu.h index 6c1a5af5f0..0b42568a4b 100644 --- a/Modules/QtWidgets/include/QmitkLevelWindowWidgetContextMenu.h +++ b/Modules/QtWidgets/include/QmitkLevelWindowWidgetContextMenu.h @@ -1,121 +1,121 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKLEVELWINDOWWIDGETCONTEXTMENU_H #define QMITKLEVELWINDOWWIDGETCONTEXTMENU_H #include #include #include #include /** * \ingroup QmitkModule * \brief Provides a contextmenu for Level/Window functionality. * * Either creates * a new contextmenu with standard functions or adds Level/Window standard * functions to an predefined contextmenu. */ class MITKQTWIDGETS_EXPORT QmitkLevelWindowWidgetContextMenu : public QWidget { Q_OBJECT public: /// constructor QmitkLevelWindowWidgetContextMenu(QWidget *parent, Qt::WindowFlags f = nullptr); ~QmitkLevelWindowWidgetContextMenu() override; /*! * data structure which reads and writes presets defined in a XML-file */ mitk::LevelWindowPreset *m_LevelWindowPreset; /*! * data structure which stores the values manipulated * by a QmitkLevelWindowWidgetContextMenu */ mitk::LevelWindow m_LevelWindow; /// submenu with all presets for contextmenu QMenu *m_PresetSubmenu; /// submenu with all images for contextmenu QMenu *m_ImageSubmenu; /// pointer to the object which manages all Level/Window changes on images and holds the LevelWindowProperty /// of the current image mitk::LevelWindowManager *m_Manager; /// map to hold all image-properties, one can get the image which is selected in the contextmenu /// with the QAction representing the image for the contextmenu std::map m_Images; /*! * returns the contextmenu with standard functions for Level/Window * * input is a prefilled contextmenu to which standard functions will be added */ - void getContextMenu(QMenu *contextmenu); + void GetContextMenu(QMenu *contextMenu); /// returns the contextmenu with standard functions for Level/Window - void getContextMenu(); + void GetContextMenu(); /// lets this object know about the LevelWindowManager to get all images and tell about changes - void setLevelWindowManager(mitk::LevelWindowManager *levelWindowManager); + void SetLevelWindowManager(mitk::LevelWindowManager *levelWindowManager); protected: - /// ID of preset selected in contextmenu - QAction *m_PresetAction; - /// ID of image selected in contextmenu - QAction *m_ImageAction; + QAction *m_PresetAction; + QAction *m_AutoTopmostAction; + QAction *m_SelectedImagesAction; -protected slots: +protected Q_SLOTS: /// sets level and window value of the current image to the values defined for the selected preset - void setPreset(QAction *presetAction); + void OnSetPreset(const QAction *presetAction); /// calls the mitkLevelWindow SetAuto method with guessByCentralSlice false, so that the greyvalues from whole image /// will be considered - void useOptimizedLevelWindow(); + void OnUseOptimizedLevelWindow(); /// calls the mitkLevelWindow SetToImageRange method, so that the greyvalues from whole image will be used - void useAllGreyvaluesFromImage(); + void OnUseAllGreyvaluesFromImage(); /// sets the level window slider to be fixed - void setFixed(); + void OnSetFixed(); /// adds a new Preset for presets-contextmenu - void addPreset(); + void OnAddPreset(); /// resets the current images Level/Window to its default values - void setDefaultLevelWindow(); + void OnSetDefaultLevelWindow(); /// resets the current images scalerange to its default values - void setDefaultScaleRange(); + void OnSetDefaultScaleRange(); /// changes the current images scalerange - void changeScaleRange(); + void OnChangeScaleRange(); /// sets the selected image or the topmost layer image to the new current image - void setImage(QAction *imageAction); + void OnSetImage(QAction *imageAction); /// sets the window to its maximum Size to fit the scalerange - void setMaximumWindow(); + void OnSetMaximumWindow(); }; -#endif + +#endif // QMITKLEVELWINDOWWIDGETCONTEXTMENU_H diff --git a/Modules/QtWidgets/include/QmitkLineEditLevelWindowWidget.h b/Modules/QtWidgets/include/QmitkLineEditLevelWindowWidget.h index 429238a86c..a01af09448 100644 --- a/Modules/QtWidgets/include/QmitkLineEditLevelWindowWidget.h +++ b/Modules/QtWidgets/include/QmitkLineEditLevelWindowWidget.h @@ -1,97 +1,97 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#ifndef QMITKLINEEDITLEVELWINDOWWIDGET -#define QMITKLINEEDITLEVELWINDOWWIDGET +#ifndef QMITKLINEEDITLEVELWINDOWWIDGET_H +#define QMITKLINEEDITLEVELWINDOWWIDGET_H #include -#include - +// mitk core #include +// qt +#include + class QmitkLevelWindowWidgetContextMenu; class QLineEdit; /** * \ingroup QmitkModule * \brief Provides a widget with two lineedit fields, one to change the * window value of the current image and one to change the level value of * the current image. */ class MITKQTWIDGETS_EXPORT QmitkLineEditLevelWindowWidget : public QWidget { Q_OBJECT public: /// constructor QmitkLineEditLevelWindowWidget(QWidget *parent = nullptr, Qt::WindowFlags f = nullptr); /// destructor ~QmitkLineEditLevelWindowWidget() override; /// inputfield for level value QLineEdit *m_LevelInput; /// inputfield for window value QLineEdit *m_WindowInput; /*! * data structure which stores the values manipulated * by a QmitkLineEditLevelWindowWidget */ mitk::LevelWindow m_LevelWindow; /// manager who is responsible to collect and deliver changes on Level/Window mitk::LevelWindowManager::Pointer m_Manager; /// sets the manager who is responsible to collect and deliver changes on Level/Window - void setLevelWindowManager(mitk::LevelWindowManager *levelWindowManager); + void SetLevelWindowManager(mitk::LevelWindowManager *levelWindowManager); /// sets the DataStorage which holds all image-nodes void SetDataStorage(mitk::DataStorage *ds); /// returns the manager who is responsible to collect and deliver changes on Level/Window mitk::LevelWindowManager *GetManager(); private: /// creates the contextmenu for this widget from class QmitkLevelWindowWidgetContextMenu void contextMenuEvent(QContextMenuEvent *) override; /// change notifications from the mitkLevelWindowManager void OnPropertyModified(const itk::EventObject &e); -public slots: +public Q_SLOTS: - /// called when return is pressed in levelinput field + /** @brief Read the levelInput and change level and slider when the button "ENTER" was pressed + * in the windowInput-LineEdit. + */ void SetLevelValue(); - - /// called when return is pressed in windowinput field + /** @brief Read the windowInput and change window and slider when the button "ENTER" was pressed + * in the windowInput-LineEdit. + */ void SetWindowValue(); - // validator to accept only possible values for Level/Window in lineedits - // void setValidator(); - protected: unsigned long m_ObserverTag; bool m_IsObserverTagSet; - /*! - * data structure which creates the contextmenu for QmitkLineEditLevelWindowWidget - */ QmitkLevelWindowWidgetContextMenu *m_Contextmenu; }; -#endif // QMITKLINEEDITLEVELWINDOWWIDGET + +#endif // QMITKLINEEDITLEVELWINDOWWIDGET_H diff --git a/Modules/QtWidgets/include/QmitkNodeDetailsDialog.h b/Modules/QtWidgets/include/QmitkNodeDetailsDialog.h index a70e0982cc..769bbf085e 100644 --- a/Modules/QtWidgets/include/QmitkNodeDetailsDialog.h +++ b/Modules/QtWidgets/include/QmitkNodeDetailsDialog.h @@ -1,56 +1,60 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITKNODEDETAILSDIALOG_H #define QMITKNODEDETAILSDIALOG_H #include #include #include class QLineEdit; class QTextBrowser; class MITKQTWIDGETS_EXPORT QmitkNodeDetailsDialog : public QDialog { Q_OBJECT public: + QmitkNodeDetailsDialog(const QList& nodes, QWidget* parent = nullptr, Qt::WindowFlags flags = nullptr); QmitkNodeDetailsDialog(const QList& nodes, QWidget* parent = nullptr, Qt::WindowFlags flags = nullptr); public Q_SLOTS: void OnSelectionChanged(const mitk::DataNode*); void OnSearchButtonClicked(bool checked = false); void OnCancelButtonClicked(bool checked = false); void KeyWordTextChanged(const QString& text); protected: bool eventFilter(QObject* obj, QEvent* event) override; protected: QLineEdit* m_KeyWord; QPushButton* m_SearchButton; QTextBrowser* m_TextBrowser; +private: + void InitWidgets(const QList& nodes); + }; #endif // QMITKNODEDETAILSDIALOG_H diff --git a/Modules/QtWidgets/include/QmitkOverlayWidget.h b/Modules/QtWidgets/include/QmitkOverlayWidget.h index 580eb64c76..34e746c9d3 100644 --- a/Modules/QtWidgets/include/QmitkOverlayWidget.h +++ b/Modules/QtWidgets/include/QmitkOverlayWidget.h @@ -1,49 +1,49 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITK_OVERLAY_WIDGET_H #define QMITK_OVERLAY_WIDGET_H #include #include /** Simple widget that can be used to achive overlays. The overlay will lie above its parent. * This implementation just renders a semi transparent black background. To add content to the * overlay derive from this class.*/ class MITKQTWIDGETS_EXPORT QmitkOverlayWidget : public QWidget { Q_OBJECT Q_PROPERTY(bool transparentForMouseEvents READ isTransparentForMouseEvents WRITE setTransparentForMouseEvents) public: explicit QmitkOverlayWidget(QWidget* parent = nullptr); virtual ~QmitkOverlayWidget(); bool isTransparentForMouseEvents() const; void setTransparentForMouseEvents(bool transparent = true); protected: - virtual bool event(QEvent* e) override; - virtual bool eventFilter(QObject* watched, QEvent* event) override; - virtual void paintEvent(QPaintEvent* event) override; + bool event(QEvent* e) override; + bool eventFilter(QObject* watched, QEvent* event) override; + void paintEvent(QPaintEvent* event) override; private: void installEventFilterOnParent(); void removeEventFilterFromParent(); }; #endif diff --git a/Modules/QtWidgets/include/QmitkSliderLevelWindowWidget.h b/Modules/QtWidgets/include/QmitkSliderLevelWindowWidget.h index 32b14887ab..624f8319ba 100644 --- a/Modules/QtWidgets/include/QmitkSliderLevelWindowWidget.h +++ b/Modules/QtWidgets/include/QmitkSliderLevelWindowWidget.h @@ -1,193 +1,192 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#ifndef QMITKSLIDERLEVELWINDOW_WIDGET -#define QMITKSLIDERLEVELWINDOW_WIDGET +#ifndef QMITKSLIDERLEVELWINDOWWIDGET_H +#define QMITKSLIDERLEVELWINDOWWIDGET_H #include #include #include class QmitkLevelWindowWidgetContextMenu; /** * \ingroup QmitkModule * * \brief Provides a widget with a slider to change the level and * window value of the current image. * * This documentation actually refers to the QmitkLevelWindowWidget * and is only put in this class due to technical issues (should be * moved later). * * The QmitkLevelWindowWidget is a kind of container for a * QmitkSliderLevelWindowWidget (this is the cyan bar above the text * input fields) and a QmitkLineEditLevelWindowWidget (with two text * input fields). It holds a reference to a mitk::LevelWindowManager * variable, which keeps the LevelWindowProperty of the currently * selected image. Level/Window is manipulated by the text inputs and * the Slider to adjust brightness/contrast of a single image. All * changes on the slider or in the text input fields affect the current * image by giving new values to LevelWindowManager. LevelWindowManager * then sends a signal to tell other listeners about changes. * * Which image is changed is determined by mitkLevelWindowManager. If * m_AutoTopMost is true, always the topmost image in data tree (layer * property) is affected by changes. The image which is affected by * changes can also be changed by QmitkLevelWindowWidgetContextMenu, * the context menu for QmitkSliderLevelWindowWidget and * QmitkLineEditLevelWindowWidget. There you have the possibility to * set a certain image or always the topmost image in the data tree * (layer property) to be affected by changes. * * The internal mitk::LevelWindow variable contains a range that is * valid for a given image. It should not be possible to move the * level/window parameters outside this range. The range can be changed * and reset to its default values by QmitkLevelWindowWidgetContextMenu, * the context menu for QmitkSliderLevelWindowWidget and * QmitkLineEditLevelWindowWidget. * * Now for the behaviour of the text inputs: The upper one contains the * value of the level (brightness), the lower one shows the window (contrast). * * The behaviour of the cyan bar is more obvious: the scale in the * background shows the valid range. The cyan bar in front displays the * currently selected level/window setting. You can change the level by * dragging the bar with the left mouse button or clicking somewhere inside * the scalerange with the left mouse button. The window is changed by * moving the mouse on the upper or lower bound of the bar until the cursor * becomes an vertical double-arrowed symbol. Then you can change the * windowsize by clicking the left mouse button and move the mouse upwards * or downwards. The bar becomes greater upwards as well as downwards. If * you want to change the size of the window in only one direction you * have to press the CTRL-key while doing the same as mentioned above. * This information is also presented by a tooltip text when moving the * mouse on the upper or lower bound of the bar. */ class MITKQTWIDGETS_EXPORT QmitkSliderLevelWindowWidget : public QWidget { Q_OBJECT public: /// constructor QmitkSliderLevelWindowWidget(QWidget *parent = nullptr, Qt::WindowFlags f = nullptr); /// destructor ~QmitkSliderLevelWindowWidget() override; - /*! - * data structure which stores the values manipulated - * by a QmitkSliderLevelWindowWidget - */ - mitk::LevelWindow m_LevelWindow; - - /// manager who is responsible to collect and deliver changes on Level/Window - mitk::LevelWindowManager::Pointer m_Manager; - /// sets the manager who is responsible to collect and deliver changes on Level/Window - void setLevelWindowManager(mitk::LevelWindowManager *levelWindowManager); + void SetLevelWindowManager(mitk::LevelWindowManager *levelWindowManager); /// sets the DataStorage which holds all image-nodes - void setDataStorage(mitk::DataStorage *ds); + void SetDataStorage(mitk::DataStorage *ds); /// returns the manager who is responsible to collect and deliver changes on Level/Window mitk::LevelWindowManager *GetManager(); + mitk::LevelWindow m_LevelWindow; + + /// manager who is responsible to collect and deliver changes on Level/Window + mitk::LevelWindowManager::Pointer m_Manager; + private: - /// creates the contextmenu for this widget from class QmitkLevelWindowWidgetContextMenu + /// creates the context menu for this widget from class QmitkLevelWindowWidgetContextMenu void contextMenuEvent(QContextMenuEvent *) override; /// change notifications from the mitkLevelWindowManager void OnPropertyModified(const itk::EventObject &e); protected: /// recalculate the size and position of the slider bar - virtual void update(); + virtual void Update(); /*! * helper for drawing the component */ QRect m_Rect; /*! * helper for drawing the component */ QPoint m_StartPos; bool m_Resize; bool m_Bottom; bool m_MouseDown; bool m_Leftbutton; bool m_CtrlPressed; int m_MoveHeight; bool m_ScaleVisible; QRect m_LowerBound; QRect m_UpperBound; unsigned long m_ObserverTag; bool m_IsObserverTagSet; QFont m_Font; /*! - * data structure which creates the contextmenu for QmitkLineEditLevelWindowWidget + * data structure which creates the context menu for QmitkLineEditLevelWindowWidget */ QmitkLevelWindowWidgetContextMenu *m_Contextmenu; /*! * repaint the slider and the scale */ void paintEvent(QPaintEvent *e) override; /*! - * method implements the component behaviour + * method implements the component behavior * * checks if cursor is on upper or lower bound of slider bar and changes cursor symbol * - * checks if left mouse button is pressed and if CTRL is pressed and changes sliderbar in movedirection accordingly + * checks if left mouse button is pressed and if CTRL is pressed and changes sliderbar in move-direction accordingly */ void mouseMoveEvent(QMouseEvent *mouseEvent) override; void enterEvent(QEvent *event) override; /*! * registers events when a mousebutton is pressed * * if leftbutton is pressed m_Leftbutton is set to true * * also checks if CTRL is pressed and sets the bool variable m_CtrlPressed */ void mousePressEvent(QMouseEvent *mouseEvent) override; /*! * sets the variable m_MouseDown to false */ void mouseReleaseEvent(QMouseEvent *mouseEvent) override; /*! * causes an update of the sliderbar when resizing the window */ void resizeEvent(QResizeEvent *event) override; -protected slots: +protected Q_SLOTS: - /// hides the scale if "Hide Scale" is selected in contextmenu - void hideScale(); + /** @brief Hide the scale if "Hide Scale" is selected in the context menu + */ + void HideScale(); - /// shows the scale if "Show Scale" is selected in contextmenu - void showScale(); + /** @brief Shows the scale if "Show Scale" is selected in the context menu + */ + void ShowScale(); }; -#endif // QMITKSLIDERLEVELWINDOW_WIDGET + +#endif // QMITKSLIDERLEVELWINDOWWIDGET_H diff --git a/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp index d83c8d96cd..b616464026 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageTreeModel.cpp @@ -1,884 +1,884 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include "QmitkDataStorageTreeModel.h" #include "QmitkDataStorageTreeModelInternalItem.h" #include "QmitkNodeDescriptorManager.h" #include #include #include #include #include #include #include #include #include QmitkDataStorageTreeModel::QmitkDataStorageTreeModel(mitk::DataStorage *_DataStorage, bool _PlaceNewNodesOnTop, QObject *parent) : QAbstractItemModel(parent), m_DataStorage(nullptr), m_PlaceNewNodesOnTop(_PlaceNewNodesOnTop), m_Root(nullptr), m_BlockDataStorageEvents(false), m_AllowHierarchyChange(false) { this->SetDataStorage(_DataStorage); } QmitkDataStorageTreeModel::~QmitkDataStorageTreeModel() { // set data storage to 0 = remove all listeners this->SetDataStorage(nullptr); m_Root->Delete(); m_Root = nullptr; } mitk::DataNode::Pointer QmitkDataStorageTreeModel::GetNode(const QModelIndex &index) const { return this->TreeItemFromIndex(index)->GetDataNode(); } const mitk::DataStorage::Pointer QmitkDataStorageTreeModel::GetDataStorage() const { return m_DataStorage.Lock(); } QModelIndex QmitkDataStorageTreeModel::index(int row, int column, const QModelIndex &parent) const { TreeItem *parentItem; if (!parent.isValid()) parentItem = m_Root; else parentItem = static_cast(parent.internalPointer()); TreeItem *childItem = parentItem->GetChild(row); if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } int QmitkDataStorageTreeModel::rowCount(const QModelIndex &parent) const { TreeItem *parentTreeItem = this->TreeItemFromIndex(parent); return parentTreeItem->GetChildCount(); } Qt::ItemFlags QmitkDataStorageTreeModel::flags(const QModelIndex &index) const { if (index.isValid()) { return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } else { return Qt::ItemIsDropEnabled; } } int QmitkDataStorageTreeModel::columnCount(const QModelIndex & /* parent = QModelIndex() */) const { return 1; } QModelIndex QmitkDataStorageTreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); TreeItem *childItem = this->TreeItemFromIndex(index); TreeItem *parentItem = childItem->GetParent(); if (parentItem == m_Root) return QModelIndex(); return this->createIndex(parentItem->GetIndex(), 0, parentItem); } QmitkDataStorageTreeModel::TreeItem *QmitkDataStorageTreeModel::TreeItemFromIndex(const QModelIndex &index) const { if (index.isValid()) return static_cast(index.internalPointer()); else return m_Root; } Qt::DropActions QmitkDataStorageTreeModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions QmitkDataStorageTreeModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } bool QmitkDataStorageTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent) { // Early exit, returning true, but not actually doing anything (ignoring data). if (action == Qt::IgnoreAction) { return true; } // Note, we are returning true if we handled it, and false otherwise bool returnValue = false; if (data->hasFormat("application/x-qabstractitemmodeldatalist")) { returnValue = true; // First we extract a Qlist of TreeItem* pointers. QList listOfItemsToDrop = ToTreeItemPtrList(data); if (listOfItemsToDrop.empty()) { return false; } // Retrieve the TreeItem* where we are dropping stuff, and its parent. TreeItem *dropItem = this->TreeItemFromIndex(parent); TreeItem *parentItem = dropItem->GetParent(); // If item was dropped onto empty space, we select the root node if (dropItem == m_Root) { parentItem = m_Root; } // Dragging and Dropping is only allowed within the same parent, so use the first item in list to validate. // (otherwise, you could have a derived image such as a segmentation, and assign it to another image). // NOTE: We are assuming the input list is valid... i.e. when it was dragged, all the items had the same parent. // Determine whether or not the drag and drop operation is a valid one. // Examples of invalid operations include: // - dragging nodes with different parents // - dragging nodes from one parent to another parent, if m_AllowHierarchyChange is false // - dragging a node on one of its child nodes (only relevant if m_AllowHierarchyChange is true) bool isValidDragAndDropOperation(true); // different parents { TreeItem *firstParent = listOfItemsToDrop[0]->GetParent(); QList::iterator diIter; for (diIter = listOfItemsToDrop.begin() + 1; diIter != listOfItemsToDrop.end(); diIter++) { if (firstParent != (*diIter)->GetParent()) { isValidDragAndDropOperation = false; break; } } } // dragging from one parent to another if ((!m_AllowHierarchyChange) && isValidDragAndDropOperation) { if (row == -1) // drag onto a node { isValidDragAndDropOperation = listOfItemsToDrop[0]->GetParent() == parentItem; } else // drag between nodes { isValidDragAndDropOperation = listOfItemsToDrop[0]->GetParent() == dropItem; } } // dragging on a child node of one the dragged nodes { QList::iterator diIter; for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++) { TreeItem *tempItem = dropItem; while (tempItem != m_Root) { tempItem = tempItem->GetParent(); if (tempItem == *diIter) { isValidDragAndDropOperation = false; } } } } if (!isValidDragAndDropOperation) return isValidDragAndDropOperation; if (listOfItemsToDrop[0] != dropItem && isValidDragAndDropOperation) { // Retrieve the index of where we are dropping stuff. QModelIndex parentModelIndex = this->IndexFromTreeItem(parentItem); int dragIndex = 0; // Iterate through the list of TreeItem (which may be at non-consecutive indexes). QList::iterator diIter; for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++) { TreeItem *itemToDrop = *diIter; // if the item is dragged down we have to compensate its final position for the // fact it is deleted lateron, this only applies if it is dragged within the same level if ((itemToDrop->GetIndex() < row) && (itemToDrop->GetParent() == dropItem)) { dragIndex = 1; } // Here we assume that as you remove items, one at a time, that GetIndex() will be valid. this->beginRemoveRows( this->IndexFromTreeItem(itemToDrop->GetParent()), itemToDrop->GetIndex(), itemToDrop->GetIndex()); itemToDrop->GetParent()->RemoveChild(itemToDrop); this->endRemoveRows(); } // row = -1 dropped on an item, row != -1 dropped in between two items // Select the target index position, or put it at the end of the list. int dropIndex = 0; if (row != -1) { if (dragIndex == 0) dropIndex = std::min(row, parentItem->GetChildCount() - 1); else dropIndex = std::min(row - 1, parentItem->GetChildCount() - 1); } else { dropIndex = dropItem->GetIndex(); } QModelIndex dropItemModelIndex = this->IndexFromTreeItem(dropItem); if ((row == -1 && dropItemModelIndex.row() == -1) || dropItemModelIndex.row() > parentItem->GetChildCount()) dropIndex = parentItem->GetChildCount() - 1; // Now insert items again at the drop item position if (m_AllowHierarchyChange) { this->beginInsertRows(dropItemModelIndex, dropIndex, dropIndex + listOfItemsToDrop.size() - 1); } else { this->beginInsertRows(parentModelIndex, dropIndex, dropIndex + listOfItemsToDrop.size() - 1); } for (diIter = listOfItemsToDrop.begin(); diIter != listOfItemsToDrop.end(); diIter++) { // dropped on node, behaviour depends on preference setting if (m_AllowHierarchyChange) { auto dataStorage = m_DataStorage.Lock(); m_BlockDataStorageEvents = true; mitk::DataNode *droppedNode = (*diIter)->GetDataNode(); mitk::DataNode *dropOntoNode = dropItem->GetDataNode(); dataStorage->Remove(droppedNode); dataStorage->Add(droppedNode, dropOntoNode); m_BlockDataStorageEvents = false; dropItem->InsertChild((*diIter), dropIndex); } else { if (row == -1) // drag onto a node { parentItem->InsertChild((*diIter), dropIndex); } else // drag between nodes { dropItem->InsertChild((*diIter), dropIndex); } } dropIndex++; } this->endInsertRows(); // Change Layers to match. this->AdjustLayerProperty(); } } else if (data->hasFormat("application/x-mitk-datanodes")) { returnValue = true; int numberOfNodesDropped = 0; QList dataNodeList = QmitkMimeTypes::ToDataNodePtrList(data); mitk::DataNode *node = nullptr; foreach (node, dataNodeList) { if (node && !m_DataStorage.IsExpired() && !m_DataStorage.Lock()->Exists(node)) { m_DataStorage.Lock()->Add(node); mitk::BaseData::Pointer basedata = node->GetData(); if (basedata.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( basedata->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); numberOfNodesDropped++; } } } // Only do a rendering update, if we actually dropped anything. if (numberOfNodesDropped > 0) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } return returnValue; } QStringList QmitkDataStorageTreeModel::mimeTypes() const { QStringList types = QAbstractItemModel::mimeTypes(); types << "application/x-qabstractitemmodeldatalist"; types << "application/x-mitk-datanodes"; return types; } QMimeData *QmitkDataStorageTreeModel::mimeData(const QModelIndexList &indexes) const { return mimeDataFromModelIndexList(indexes); } QMimeData *QmitkDataStorageTreeModel::mimeDataFromModelIndexList(const QModelIndexList &indexes) { QMimeData *ret = new QMimeData; QString treeItemAddresses(""); QString dataNodeAddresses(""); QByteArray baTreeItemPtrs; QByteArray baDataNodePtrs; QDataStream dsTreeItemPtrs(&baTreeItemPtrs, QIODevice::WriteOnly); QDataStream dsDataNodePtrs(&baDataNodePtrs, QIODevice::WriteOnly); for (int i = 0; i < indexes.size(); i++) { TreeItem *treeItem = static_cast(indexes.at(i).internalPointer()); dsTreeItemPtrs << reinterpret_cast(treeItem); - dsDataNodePtrs << reinterpret_cast(treeItem->GetDataNode()); + dsDataNodePtrs << reinterpret_cast(treeItem->GetDataNode().GetPointer()); // --------------- deprecated ----------------- unsigned long long treeItemAddress = reinterpret_cast(treeItem); - unsigned long long dataNodeAddress = reinterpret_cast(treeItem->GetDataNode()); + unsigned long long dataNodeAddress = reinterpret_cast(treeItem->GetDataNode().GetPointer()); QTextStream(&treeItemAddresses) << treeItemAddress; QTextStream(&dataNodeAddresses) << dataNodeAddress; if (i != indexes.size() - 1) { QTextStream(&treeItemAddresses) << ","; QTextStream(&dataNodeAddresses) << ","; } // -------------- end deprecated ------------- } // ------------------ deprecated ----------------- ret->setData("application/x-qabstractitemmodeldatalist", QByteArray(treeItemAddresses.toLatin1())); ret->setData("application/x-mitk-datanodes", QByteArray(dataNodeAddresses.toLatin1())); // --------------- end deprecated ----------------- ret->setData(QmitkMimeTypes::DataStorageTreeItemPtrs, baTreeItemPtrs); ret->setData(QmitkMimeTypes::DataNodePtrs, baDataNodePtrs); return ret; } QVariant QmitkDataStorageTreeModel::data(const QModelIndex &index, int role) const { mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode(); // get name of treeItem (may also be edited) QString nodeName = QString::fromStdString(dataNode->GetName()); if (nodeName.isEmpty()) { nodeName = "unnamed"; } if (role == Qt::DisplayRole) return nodeName; else if (role == Qt::ToolTipRole) return nodeName; else if (role == Qt::DecorationRole) { QmitkNodeDescriptor *nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); return nodeDescriptor->GetIcon(dataNode); } else if (role == Qt::CheckStateRole) { return dataNode->IsVisible(nullptr); } else if (role == QmitkDataNodeRole) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } else if (role == QmitkDataNodeRawPointerRole) { return QVariant::fromValue(dataNode); } return QVariant(); } bool QmitkDataStorageTreeModel::DicomPropertiesExists(const mitk::DataNode &node) const { bool propertiesExists = false; mitk::BaseProperty *seriesDescription_deprecated = (node.GetProperty("dicom.series.SeriesDescription")); mitk::BaseProperty *studyDescription_deprecated = (node.GetProperty("dicom.study.StudyDescription")); mitk::BaseProperty *patientsName_deprecated = (node.GetProperty("dicom.patient.PatientsName")); mitk::BaseProperty *seriesDescription = (node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x103e).c_str())); mitk::BaseProperty *studyDescription = (node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x1030).c_str())); mitk::BaseProperty *patientsName = (node.GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0010, 0x0010).c_str())); if (patientsName != nullptr && studyDescription != nullptr && seriesDescription != nullptr) { if ((!patientsName->GetValueAsString().empty()) && (!studyDescription->GetValueAsString().empty()) && (!seriesDescription->GetValueAsString().empty())) { propertiesExists = true; } } /** Code coveres the deprecated property naming for backwards compatibility */ if (patientsName_deprecated != nullptr && studyDescription_deprecated != nullptr && seriesDescription_deprecated != nullptr) { if ((!patientsName_deprecated->GetValueAsString().empty()) && (!studyDescription_deprecated->GetValueAsString().empty()) && (!seriesDescription_deprecated->GetValueAsString().empty())) { propertiesExists = true; } } return propertiesExists; } QVariant QmitkDataStorageTreeModel::headerData(int /*section*/, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole && m_Root) return QString::fromStdString(m_Root->GetDataNode()->GetName()); return QVariant(); } void QmitkDataStorageTreeModel::SetDataStorage(mitk::DataStorage *_DataStorage) { if (m_DataStorage != _DataStorage) // dont take the same again { if (!m_DataStorage.IsExpired()) { auto dataStorage = m_DataStorage.Lock(); // remove Listener for the data storage itself dataStorage->RemoveObserver(m_DataStorageDeletedTag); // remove listeners for the nodes dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkDataStorageTreeModel::AddNode)); dataStorage->ChangedNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetNodeModified)); dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::RemoveNode)); } // take over the new data storage m_DataStorage = _DataStorage; // delete the old root (if necessary, create new) if (m_Root) m_Root->Delete(); mitk::DataNode::Pointer rootDataNode = mitk::DataNode::New(); rootDataNode->SetName("Data Manager"); m_Root = new TreeItem(rootDataNode, nullptr); this->beginResetModel(); this->endResetModel(); if (!m_DataStorage.IsExpired()) { auto dataStorage = m_DataStorage.Lock(); // add Listener for the data storage itself auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkDataStorageTreeModel::SetDataStorageDeleted); m_DataStorageDeletedTag = dataStorage->AddObserver(itk::DeleteEvent(), command); // add listeners for the nodes dataStorage->AddNodeEvent.AddListener(mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::AddNode)); dataStorage->ChangedNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::SetNodeModified)); dataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageTreeModel::RemoveNode)); mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = dataStorage->GetSubset(m_Predicate); // finally add all nodes to the model this->Update(); } } } void QmitkDataStorageTreeModel::SetDataStorageDeleted() { this->SetDataStorage(nullptr); } void QmitkDataStorageTreeModel::AddNodeInternal(const mitk::DataNode *node) { if (node == nullptr || m_DataStorage.IsExpired() || !m_DataStorage.Lock()->Exists(node) || m_Root->Find(node) != nullptr) return; // find out if we have a root node TreeItem *parentTreeItem = m_Root; QModelIndex index; mitk::DataNode *parentDataNode = this->GetParentNode(node); if (parentDataNode) // no top level data node { parentTreeItem = m_Root->Find(parentDataNode); // find the corresponding tree item if (!parentTreeItem) { this->AddNode(parentDataNode); parentTreeItem = m_Root->Find(parentDataNode); if (!parentTreeItem) return; } // get the index of this parent with the help of the grand parent index = this->createIndex(parentTreeItem->GetIndex(), 0, parentTreeItem); } // add node if (m_PlaceNewNodesOnTop) { // emit beginInsertRows event beginInsertRows(index, 0, 0); parentTreeItem->InsertChild(new TreeItem(const_cast(node)), 0); } else { int firstRowWithASiblingBelow = 0; int nodeLayer = -1; node->GetIntProperty("layer", nodeLayer); for (TreeItem* siblingTreeItem: parentTreeItem->GetChildren()) { int siblingLayer = -1; if (mitk::DataNode* siblingNode = siblingTreeItem->GetDataNode()) { siblingNode->GetIntProperty("layer", siblingLayer); } if (nodeLayer > siblingLayer) { break; } ++firstRowWithASiblingBelow; } beginInsertRows(index, firstRowWithASiblingBelow, firstRowWithASiblingBelow); parentTreeItem->InsertChild(new TreeItem(const_cast(node)), firstRowWithASiblingBelow); } // emit endInsertRows event endInsertRows(); if(m_PlaceNewNodesOnTop) { this->AdjustLayerProperty(); } } void QmitkDataStorageTreeModel::AddNode(const mitk::DataNode *node) { if (node == nullptr || m_BlockDataStorageEvents || m_DataStorage.IsExpired() || !m_DataStorage.Lock()->Exists(node) || m_Root->Find(node) != nullptr) return; this->AddNodeInternal(node); } void QmitkDataStorageTreeModel::SetPlaceNewNodesOnTop(bool _PlaceNewNodesOnTop) { m_PlaceNewNodesOnTop = _PlaceNewNodesOnTop; } void QmitkDataStorageTreeModel::RemoveNodeInternal(const mitk::DataNode *node) { if (!m_Root) return; TreeItem *treeItem = m_Root->Find(node); if (!treeItem) return; // return because there is no treeitem containing this node TreeItem *parentTreeItem = treeItem->GetParent(); QModelIndex parentIndex = this->IndexFromTreeItem(parentTreeItem); // emit beginRemoveRows event (QModelIndex is empty because we dont have a tree model) this->beginRemoveRows(parentIndex, treeItem->GetIndex(), treeItem->GetIndex()); // remove node std::vector children = treeItem->GetChildren(); delete treeItem; // emit endRemoveRows event endRemoveRows(); // move all children of deleted node into its parent for (std::vector::iterator it = children.begin(); it != children.end(); it++) { // emit beginInsertRows event beginInsertRows(parentIndex, parentTreeItem->GetChildCount(), parentTreeItem->GetChildCount()); // add nodes again parentTreeItem->AddChild(*it); // emit endInsertRows event endInsertRows(); } this->AdjustLayerProperty(); } void QmitkDataStorageTreeModel::RemoveNode(const mitk::DataNode *node) { if (node == nullptr || m_BlockDataStorageEvents) return; this->RemoveNodeInternal(node); } void QmitkDataStorageTreeModel::SetNodeModified(const mitk::DataNode *node) { TreeItem *treeItem = m_Root->Find(node); if (treeItem) { TreeItem *parentTreeItem = treeItem->GetParent(); // as the root node should not be removed one should always have a parent item if (!parentTreeItem) return; QModelIndex index = this->createIndex(treeItem->GetIndex(), 0, treeItem); // now emit the dataChanged signal emit dataChanged(index, index); } } mitk::DataNode *QmitkDataStorageTreeModel::GetParentNode(const mitk::DataNode *node) const { mitk::DataNode *dataNode = nullptr; mitk::DataStorage::SetOfObjects::ConstPointer _Sources = m_DataStorage.Lock()->GetSources(node); if (_Sources->Size() > 0) dataNode = _Sources->front(); return dataNode; } bool QmitkDataStorageTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) { mitk::DataNode *dataNode = this->TreeItemFromIndex(index)->GetDataNode(); if (!dataNode) return false; if (role == Qt::EditRole && !value.toString().isEmpty()) { dataNode->SetStringProperty("name", value.toString().toStdString().c_str()); mitk::PlanarFigure *planarFigure = dynamic_cast(dataNode->GetData()); if (planarFigure != nullptr) mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (role == Qt::CheckStateRole) { // Please note: value.toInt() returns 2, independentely from the actual checkstate of the index element. // Therefore the checkstate is being estimated again here. QVariant qcheckstate = index.data(Qt::CheckStateRole); int checkstate = qcheckstate.toInt(); bool isVisible = bool(checkstate); dataNode->SetVisibility(!isVisible); emit nodeVisibilityChanged(); } // inform listeners about changes emit dataChanged(index, index); return true; } bool QmitkDataStorageTreeModel::setHeaderData(int /*section*/, Qt::Orientation /*orientation*/, const QVariant & /* value */, int /*role = Qt::EditRole*/) { return false; } void QmitkDataStorageTreeModel::AdjustLayerProperty() { /// transform the tree into an array and set the layer property descending std::vector vec; this->TreeToVector(m_Root, vec); int i = vec.size() - 1; for (std::vector::const_iterator it = vec.begin(); it != vec.end(); ++it) { mitk::DataNode::Pointer dataNode = (*it)->GetDataNode(); bool fixedLayer = false; if (!(dataNode->GetBoolProperty("fixedLayer", fixedLayer) && fixedLayer)) dataNode->SetIntProperty("layer", i); --i; } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataStorageTreeModel::TreeToVector(TreeItem *parent, std::vector &vec) const { TreeItem *current; for (int i = 0; i < parent->GetChildCount(); ++i) { current = parent->GetChild(i); this->TreeToVector(current, vec); vec.push_back(current); } } QModelIndex QmitkDataStorageTreeModel::IndexFromTreeItem(TreeItem *item) const { if (item == m_Root) return QModelIndex(); else return this->createIndex(item->GetIndex(), 0, item); } QList QmitkDataStorageTreeModel::GetNodeSet() const { QList res; if (m_Root) this->TreeToNodeSet(m_Root, res); return res; } void QmitkDataStorageTreeModel::TreeToNodeSet(TreeItem *parent, QList &vec) const { TreeItem *current; for (int i = 0; i < parent->GetChildCount(); ++i) { current = parent->GetChild(i); vec.push_back(current->GetDataNode()); this->TreeToNodeSet(current, vec); } } QModelIndex QmitkDataStorageTreeModel::GetIndex(const mitk::DataNode *node) const { if (m_Root) { TreeItem *item = m_Root->Find(node); if (item) return this->IndexFromTreeItem(item); } return QModelIndex(); } QList QmitkDataStorageTreeModel::ToTreeItemPtrList(const QMimeData *mimeData) { if (mimeData == nullptr || !mimeData->hasFormat(QmitkMimeTypes::DataStorageTreeItemPtrs)) { return QList(); } return ToTreeItemPtrList(mimeData->data(QmitkMimeTypes::DataStorageTreeItemPtrs)); } QList QmitkDataStorageTreeModel::ToTreeItemPtrList(const QByteArray &ba) { QList result; QDataStream ds(ba); while (!ds.atEnd()) { quintptr treeItemPtr; ds >> treeItemPtr; result.push_back(reinterpret_cast(treeItemPtr)); } return result; } void QmitkDataStorageTreeModel::Update() { if (!m_DataStorage.IsExpired()) { mitk::DataStorage::SetOfObjects::ConstPointer _NodeSet = m_DataStorage.Lock()->GetAll(); /// Regardless the value of this preference, the new nodes must not be inserted /// at the top now, but at the position according to their layer. bool newNodesWereToBePlacedOnTop = m_PlaceNewNodesOnTop; m_PlaceNewNodesOnTop = false; for (const auto& node: *_NodeSet) { this->AddNodeInternal(node); } m_PlaceNewNodesOnTop = newNodesWereToBePlacedOnTop; /// Adjust the layers to ensure that derived nodes are above their sources. this->AdjustLayerProperty(); } } void QmitkDataStorageTreeModel::SetAllowHierarchyChange(bool allowHierarchyChange) { m_AllowHierarchyChange = allowHierarchyChange; } diff --git a/Modules/QtWidgets/src/QmitkDataStorageTreeModelInternalItem.cpp b/Modules/QtWidgets/src/QmitkDataStorageTreeModelInternalItem.cpp index ae7741b899..bc6ddb599f 100644 --- a/Modules/QtWidgets/src/QmitkDataStorageTreeModelInternalItem.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageTreeModelInternalItem.cpp @@ -1,144 +1,144 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkDataStorageTreeModelInternalItem.h" #include "QmitkNodeDescriptorManager.h" #include #include #include QmitkDataStorageTreeModelInternalItem::QmitkDataStorageTreeModelInternalItem(mitk::DataNode *_DataNode, QmitkDataStorageTreeModelInternalItem *_Parent) : m_Parent(_Parent), m_DataNode(_DataNode) { if (m_Parent) m_Parent->AddChild(this); } QmitkDataStorageTreeModelInternalItem::~QmitkDataStorageTreeModelInternalItem() { if (m_Parent) m_Parent->RemoveChild(this); } void QmitkDataStorageTreeModelInternalItem::Delete() { while (m_Children.size() > 0) delete m_Children.back(); delete this; } QmitkDataStorageTreeModelInternalItem *QmitkDataStorageTreeModelInternalItem::Find(const mitk::DataNode *_DataNode) const { QmitkDataStorageTreeModelInternalItem *item = nullptr; if (_DataNode) { if (m_DataNode == _DataNode) item = const_cast(this); else { for (std::vector::const_iterator it = m_Children.begin(); it != m_Children.end(); ++it) { if (item) break; item = (*it)->Find(_DataNode); } } } return item; } int QmitkDataStorageTreeModelInternalItem::IndexOfChild(const QmitkDataStorageTreeModelInternalItem *item) const { std::vector::const_iterator it = std::find(m_Children.begin(), m_Children.end(), item); return it != m_Children.end() ? std::distance(m_Children.begin(), it) : -1; } QmitkDataStorageTreeModelInternalItem *QmitkDataStorageTreeModelInternalItem::GetChild(int index) const { return (m_Children.size() > 0 && index >= 0 && index < (int)m_Children.size()) ? m_Children.at(index) : 0; } void QmitkDataStorageTreeModelInternalItem::AddChild(QmitkDataStorageTreeModelInternalItem *item) { this->InsertChild(item); } void QmitkDataStorageTreeModelInternalItem::RemoveChild(QmitkDataStorageTreeModelInternalItem *item) { std::vector::iterator it = std::find(m_Children.begin(), m_Children.end(), item); if (it != m_Children.end()) { m_Children.erase(it); item->SetParent(0); } } int QmitkDataStorageTreeModelInternalItem::GetChildCount() const { return m_Children.size(); } int QmitkDataStorageTreeModelInternalItem::GetIndex() const { if (m_Parent) return m_Parent->IndexOfChild(this); return 0; } QmitkDataStorageTreeModelInternalItem *QmitkDataStorageTreeModelInternalItem::GetParent() const { return m_Parent; } -mitk::DataNode* QmitkDataStorageTreeModelInternalItem::GetDataNode() const +mitk::DataNode::Pointer QmitkDataStorageTreeModelInternalItem::GetDataNode() const { - return m_DataNode; + return m_DataNode.Lock(); } void QmitkDataStorageTreeModelInternalItem::InsertChild(QmitkDataStorageTreeModelInternalItem *item, int index) { std::vector::iterator it = std::find(m_Children.begin(), m_Children.end(), item); if (it == m_Children.end()) { if (m_Children.size() > 0 && index >= 0 && index < (int)m_Children.size()) { it = m_Children.begin(); std::advance(it, index); m_Children.insert(it, item); } else m_Children.push_back(item); // add parent if necessary if (item->GetParent() != this) item->SetParent(this); } } std::vector QmitkDataStorageTreeModelInternalItem::GetChildren() const { return m_Children; } void QmitkDataStorageTreeModelInternalItem::SetParent(QmitkDataStorageTreeModelInternalItem *_Parent) { m_Parent = _Parent; if (m_Parent) m_Parent->AddChild(this); } diff --git a/Modules/QtWidgets/src/QmitkLevelWindowWidget.cpp b/Modules/QtWidgets/src/QmitkLevelWindowWidget.cpp index a70e5b0151..d6805560a9 100644 --- a/Modules/QtWidgets/src/QmitkLevelWindowWidget.cpp +++ b/Modules/QtWidgets/src/QmitkLevelWindowWidget.cpp @@ -1,37 +1,37 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkLevelWindowWidget.h" #include "QmitkSliderLevelWindowWidget.h" QmitkLevelWindowWidget::QmitkLevelWindowWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { this->setupUi(this); m_Manager = mitk::LevelWindowManager::New(); - SliderLevelWindowWidget->setLevelWindowManager(m_Manager.GetPointer()); - LineEditLevelWindowWidget->setLevelWindowManager(m_Manager.GetPointer()); + SliderLevelWindowWidget->SetLevelWindowManager(m_Manager.GetPointer()); + LineEditLevelWindowWidget->SetLevelWindowManager(m_Manager.GetPointer()); } void QmitkLevelWindowWidget::SetDataStorage(mitk::DataStorage *ds) { m_Manager->SetDataStorage(ds); } mitk::LevelWindowManager *QmitkLevelWindowWidget::GetManager() { return m_Manager.GetPointer(); } diff --git a/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp b/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp index 059c0c8189..3676429edf 100644 --- a/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp +++ b/Modules/QtWidgets/src/QmitkLevelWindowWidgetContextMenu.cpp @@ -1,252 +1,305 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ +#include + +// mitk core +#include + +// mitk qt widgets #include "QmitkLevelWindowPresetDefinitionDialog.h" #include "QmitkLevelWindowRangeChangeDialog.h" + +// qt #include -#include -#include QmitkLevelWindowWidgetContextMenu::QmitkLevelWindowWidgetContextMenu(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { m_LevelWindowPreset = mitk::LevelWindowPreset::New(); m_LevelWindowPreset->LoadPreset(); } QmitkLevelWindowWidgetContextMenu::~QmitkLevelWindowWidgetContextMenu() { m_LevelWindowPreset->Delete(); } -void QmitkLevelWindowWidgetContextMenu::setPreset(QAction *presetAction) +void QmitkLevelWindowWidgetContextMenu::OnSetPreset(const QAction *presetAction) { QString item = presetAction->text(); if (!(presetAction == m_PresetAction)) { double dlevel = m_LevelWindowPreset->getLevel(item.toStdString()); double dwindow = m_LevelWindowPreset->getWindow(item.toStdString()); if ((dlevel + dwindow / 2) > m_LevelWindow.GetRangeMax()) { double lowerBound = (dlevel - dwindow / 2); if (!(lowerBound > m_LevelWindow.GetRangeMax())) { dwindow = m_LevelWindow.GetRangeMax() - lowerBound; dlevel = lowerBound + dwindow / 2; } else { dlevel = m_LevelWindow.GetRangeMax() - 1; dwindow = 2; } } else if ((dlevel - dwindow / 2) < m_LevelWindow.GetRangeMin()) { double upperBound = (dlevel + dwindow / 2); if (!(upperBound < m_LevelWindow.GetRangeMin())) { dwindow = m_LevelWindow.GetRangeMin() + upperBound; dlevel = upperBound - dwindow / 2; } else { dlevel = m_LevelWindow.GetRangeMin() + 1; dwindow = 2; } } m_LevelWindow.SetLevelWindow(dlevel, dwindow); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } -void QmitkLevelWindowWidgetContextMenu::setLevelWindowManager(mitk::LevelWindowManager *levelWindowManager) +void QmitkLevelWindowWidgetContextMenu::SetLevelWindowManager(mitk::LevelWindowManager *levelWindowManager) { m_Manager = levelWindowManager; } -void QmitkLevelWindowWidgetContextMenu::addPreset() +void QmitkLevelWindowWidgetContextMenu::OnAddPreset() { QmitkLevelWindowPresetDefinitionDialog addPreset(this); addPreset.setPresets(m_LevelWindowPreset->getLevelPresets(), m_LevelWindowPreset->getWindowPresets(), QString::number((int)m_LevelWindow.GetLevel()), QString::number((int)m_LevelWindow.GetWindow())); if (addPreset.exec()) { m_LevelWindowPreset->newPresets(addPreset.getLevelPresets(), addPreset.getWindowPresets()); } } -void QmitkLevelWindowWidgetContextMenu::setFixed() +void QmitkLevelWindowWidgetContextMenu::OnSetFixed() { m_LevelWindow.SetFixed(!m_LevelWindow.GetFixed()); m_Manager->SetLevelWindow(m_LevelWindow); } -void QmitkLevelWindowWidgetContextMenu::useAllGreyvaluesFromImage() +void QmitkLevelWindowWidgetContextMenu::OnUseAllGreyvaluesFromImage() { m_LevelWindow.SetToImageRange(m_Manager->GetCurrentImage()); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } -void QmitkLevelWindowWidgetContextMenu::useOptimizedLevelWindow() +void QmitkLevelWindowWidgetContextMenu::OnUseOptimizedLevelWindow() { m_LevelWindow.SetAuto(m_Manager->GetCurrentImage(), false, false); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } -void QmitkLevelWindowWidgetContextMenu::setDefaultLevelWindow() +void QmitkLevelWindowWidgetContextMenu::OnSetDefaultLevelWindow() { m_LevelWindow.ResetDefaultLevelWindow(); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } -void QmitkLevelWindowWidgetContextMenu::setMaximumWindow() +void QmitkLevelWindowWidgetContextMenu::OnSetMaximumWindow() { m_LevelWindow.SetToMaxWindowSize(); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } -void QmitkLevelWindowWidgetContextMenu::setDefaultScaleRange() +void QmitkLevelWindowWidgetContextMenu::OnSetDefaultScaleRange() { m_LevelWindow.ResetDefaultRangeMinMax(); m_LevelWindow.SetLevelWindow(m_LevelWindow.GetLevel(), m_LevelWindow.GetWindow()); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } -void QmitkLevelWindowWidgetContextMenu::changeScaleRange() +void QmitkLevelWindowWidgetContextMenu::OnChangeScaleRange() { QmitkLevelWindowRangeChangeDialog changeRange(this); changeRange.setLowerLimit((mitk::ScalarType)m_LevelWindow.GetRangeMin()); changeRange.setUpperLimit((mitk::ScalarType)m_LevelWindow.GetRangeMax()); if (changeRange.exec()) { m_LevelWindow.SetRangeMinMax(changeRange.getLowerLimit(), changeRange.getUpperLimit()); m_LevelWindow.SetLevelWindow(m_LevelWindow.GetLevel(), m_LevelWindow.GetWindow()); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } -void QmitkLevelWindowWidgetContextMenu::setImage(QAction *imageAction) +void QmitkLevelWindowWidgetContextMenu::OnSetImage(QAction *imageAction) { - if (imageAction == m_ImageAction) - if (m_Manager->isAutoTopMost() == false) + if (imageAction == m_AutoTopmostAction) + { + if (m_Manager->IsAutoTopMost() == false) + { m_Manager->SetAutoTopMostImage(true); + } else + { m_Manager->SetAutoTopMostImage(false); + } + } + else if(imageAction == m_SelectedImagesAction) + { + if (m_Manager->IsSelectedImages() == false) + { + m_Manager->SetSelectedImages(true); + } + else + { + m_Manager->SetSelectedImages(false); + } + } else - m_Manager->SetLevelWindowProperty(m_Images[imageAction]); + { + m_Manager->SetLevelWindowProperty(m_Images.at(imageAction)); + } } -void QmitkLevelWindowWidgetContextMenu::getContextMenu(QMenu *contextmenu) +void QmitkLevelWindowWidgetContextMenu::GetContextMenu(QMenu *contextMenu) { + if (nullptr == contextMenu) + { + return; + } + try { m_LevelWindow = m_Manager->GetLevelWindow(); - QMenu *contextMenu = contextmenu; - Q_CHECK_PTR(contextMenu); - // contextMenu->setCheckable(true); - QAction *sliderFixed = contextMenu->addAction(tr("Set Slider Fixed"), this, SLOT(setFixed())); + QAction *sliderFixed = contextMenu->addAction(tr("Set slider fixed"), this, &QmitkLevelWindowWidgetContextMenu::OnSetFixed); sliderFixed->setCheckable(true); sliderFixed->setChecked(m_LevelWindow.IsFixed()); contextMenu->addSeparator(); - contextMenu->addAction(tr("Use whole image grey values"), this, SLOT(useAllGreyvaluesFromImage())); - contextMenu->addAction(tr("Use optimized levelwindow"), this, SLOT(useOptimizedLevelWindow())); + contextMenu->addAction(tr("Use whole image grey values"), this, &QmitkLevelWindowWidgetContextMenu::OnUseAllGreyvaluesFromImage); + contextMenu->addAction(tr("Use optimized level-window"), this, &QmitkLevelWindowWidgetContextMenu::OnUseOptimizedLevelWindow); contextMenu->addSeparator(); - contextMenu->addAction(tr("Set Maximum Window"), this, SLOT(setMaximumWindow())); - contextMenu->addAction(tr("Default Level/Window"), this, SLOT(setDefaultLevelWindow())); + contextMenu->addAction(tr("Set maximum window"), this, &QmitkLevelWindowWidgetContextMenu::OnSetMaximumWindow); + contextMenu->addAction(tr("Default level-window"), this, &QmitkLevelWindowWidgetContextMenu::OnSetDefaultLevelWindow); contextMenu->addSeparator(); - contextMenu->addAction(tr("Change Scale Range"), this, SLOT(changeScaleRange())); - contextMenu->addAction(tr("Default Scale Range"), this, SLOT(setDefaultScaleRange())); + contextMenu->addAction(tr("Change scale range"), this, &QmitkLevelWindowWidgetContextMenu::OnChangeScaleRange); + contextMenu->addAction(tr("Default scale range"), this, &QmitkLevelWindowWidgetContextMenu::OnSetDefaultScaleRange); contextMenu->addSeparator(); m_PresetSubmenu = new QMenu(this); - Q_CHECK_PTR(m_PresetSubmenu); m_PresetSubmenu->setTitle("Presets"); - m_PresetAction = m_PresetSubmenu->addAction(tr("Preset Definition"), this, SLOT(addPreset())); + m_PresetAction = m_PresetSubmenu->addAction(tr("Preset definition"), this, &QmitkLevelWindowWidgetContextMenu::OnAddPreset); m_PresetSubmenu->addSeparator(); std::map preset = m_LevelWindowPreset->getLevelPresets(); for (auto iter = preset.begin(); iter != preset.end(); iter++) { QString item = ((*iter).first.c_str()); m_PresetSubmenu->addAction(item); } - connect(m_PresetSubmenu, SIGNAL(triggered(QAction *)), this, SLOT(setPreset(QAction *))); + + connect(m_PresetSubmenu, &QMenu::triggered, this, &QmitkLevelWindowWidgetContextMenu::OnSetPreset); contextMenu->addMenu(m_PresetSubmenu); contextMenu->addSeparator(); - m_ImageSubmenu = new QMenu(this); m_ImageSubmenu->setTitle("Images"); - // m_ImageSubmenu->setCheckable(true); - m_ImageAction = m_ImageSubmenu->addAction(tr("Set Topmost Image")); - m_ImageAction->setCheckable(true); - if (m_Manager->isAutoTopMost()) - m_ImageAction->setChecked(true); + + // add action for "auto topmost image" action + m_AutoTopmostAction = m_ImageSubmenu->addAction(tr("Set topmost image")); + m_AutoTopmostAction->setCheckable(true); + if (m_Manager->IsAutoTopMost()) + { + m_AutoTopmostAction->setChecked(true); + } + + // add action for "selected images" action m_ImageSubmenu->addSeparator(); - Q_CHECK_PTR(m_ImageSubmenu); + m_SelectedImagesAction = m_ImageSubmenu->addAction(tr("Use selected images")); + m_SelectedImagesAction->setCheckable(true); + if (m_Manager->IsSelectedImages()) + { + m_SelectedImagesAction->setChecked(true); + } + + // add action for individual images + m_ImageSubmenu->addSeparator(); + mitk::DataStorage::SetOfObjects::ConstPointer allObjects = m_Manager->GetRelevantNodes(); for (mitk::DataStorage::SetOfObjects::ConstIterator objectIter = allObjects->Begin(); - objectIter != allObjects->End(); - ++objectIter) + objectIter != allObjects->End(); + ++objectIter) { mitk::DataNode *node = objectIter->Value(); - if (node) + if (nullptr == node) { - if (node->IsVisible(nullptr) == false) - continue; - mitk::LevelWindowProperty::Pointer levelWindowProperty = - dynamic_cast(node->GetProperty("levelwindow")); - bool isHelperObject = false; - node->GetBoolProperty("helper object", isHelperObject); - if (levelWindowProperty.IsNotNull() && !isHelperObject) + continue; + } + + bool isHelperObject = false; + node->GetBoolProperty("helper object", isHelperObject); + + if (isHelperObject) + { + continue; + } + + if (!node->IsVisible(nullptr)) + { + continue; + } + + mitk::LevelWindowProperty::Pointer levelWindowProperty = + dynamic_cast(node->GetProperty("levelwindow")); + + if (levelWindowProperty.IsNotNull()) + { + std::string name; + node->GetName(name); + QString item = name.c_str(); + QAction *id = m_ImageSubmenu->addAction(item); + id->setCheckable(true); + m_Images[id] = levelWindowProperty; + if (levelWindowProperty == m_Manager->GetLevelWindowProperty()) { - std::string name; - node->GetName(name); - QString item = name.c_str(); - QAction *id = m_ImageSubmenu->addAction(item); - id->setCheckable(true); - m_Images[id] = levelWindowProperty; - if (levelWindowProperty == m_Manager->GetLevelWindowProperty()) - { - id->setChecked(true); - } + id->setChecked(true); } } } - connect(m_ImageSubmenu, SIGNAL(triggered(QAction *)), this, SLOT(setImage(QAction *))); - contextMenu->addMenu(m_ImageSubmenu); + connect(m_ImageSubmenu, &QMenu::triggered, this, &QmitkLevelWindowWidgetContextMenu::OnSetImage); + + contextMenu->addMenu(m_ImageSubmenu); contextMenu->exec(QCursor::pos()); } catch (...) { } } -void QmitkLevelWindowWidgetContextMenu::getContextMenu() +void QmitkLevelWindowWidgetContextMenu::GetContextMenu() { auto contextMenu = new QMenu(this); - getContextMenu(contextMenu); + GetContextMenu(contextMenu); delete contextMenu; } diff --git a/Modules/QtWidgets/src/QmitkLineEditLevelWindowWidget.cpp b/Modules/QtWidgets/src/QmitkLineEditLevelWindowWidget.cpp index be5ad4ac87..6bb58cb3d3 100644 --- a/Modules/QtWidgets/src/QmitkLineEditLevelWindowWidget.cpp +++ b/Modules/QtWidgets/src/QmitkLineEditLevelWindowWidget.cpp @@ -1,182 +1,172 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkLineEditLevelWindowWidget.h" -#include "QmitkLevelWindowWidgetContextMenu.h" +// mitk core #include +// mitk qt widgets +#include + +// qt #include #include #include + +// itk #include + +// c++ #include #include -using namespace std; - -/** -* Constructor -*/ QmitkLineEditLevelWindowWidget::QmitkLineEditLevelWindowWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { m_Manager = mitk::LevelWindowManager::New(); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmitkLineEditLevelWindowWidget::OnPropertyModified); m_ObserverTag = m_Manager->AddObserver(itk::ModifiedEvent(), command); m_IsObserverTagSet = true; - m_Contextmenu = new QmitkLevelWindowWidgetContextMenu(this); // true); + m_Contextmenu = new QmitkLevelWindowWidgetContextMenu(this); auto layout = new QVBoxLayout(this); layout->setMargin(0); layout->setSpacing(0); m_LevelInput = new QLineEdit(this); m_LevelInput->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); m_LevelInput->setToolTip("Edit this field to change the center of the levelwindow."); - // m_LevelInput->setFrameShape( QLineEdit::LineEditPanel ); - // m_LevelInput->setFrameShadow( QLineEdit::Sunken ); m_WindowInput = new QLineEdit(this); m_WindowInput->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); m_WindowInput->setToolTip( "Edit this field to change the span of the levelwindow. This number describes the whole span around the center."); - // m_WindowInput->setFrameShape( QLineEdit::LineEditPanel ); - // m_WindowInput->setFrameShadow( QLineEdit::Sunken ); layout->addWidget(m_LevelInput); layout->addWidget(m_WindowInput); // signals and slots connections connect(m_LevelInput, SIGNAL(editingFinished()), this, SLOT(SetLevelValue())); connect(m_WindowInput, SIGNAL(editingFinished()), this, SLOT(SetWindowValue())); // Validator for both LineEdit-widgets, to limit the valid input-range to int. - // QValidator* validatorWindowInput = new QIntValidator(1, 20000000, this); - QValidator *validatorWindowInput = new QDoubleValidator(0, numeric_limits::max(), 2, this); + QValidator *validatorWindowInput = new QDoubleValidator(0, std::numeric_limits::max(), 2, this); m_WindowInput->setValidator(validatorWindowInput); - // QValidator* validatorLevelInput = new QIntValidator(-10000000, 10000000, this); - // QValidator* validatorLevelInput = new QDoubleValidator(numeric_limits::min(), - // numeric_limits::max(), 2, this); - // m_LevelInput->setValidator(validatorLevelInput); - this->hide(); } QmitkLineEditLevelWindowWidget::~QmitkLineEditLevelWindowWidget() { if (m_IsObserverTagSet) { m_Manager->RemoveObserver(m_ObserverTag); m_IsObserverTagSet = false; } } void QmitkLineEditLevelWindowWidget::OnPropertyModified(const itk::EventObject &) { try { m_LevelWindow = m_Manager->GetLevelWindow(); - // setValidator(); QString level; QString window; if (m_LevelWindow.IsFloatingValues()) { std::stringstream ssLevel; std::stringstream ssWindow; ssLevel << std::setprecision(3) << m_LevelWindow.GetLevel(); ssWindow << std::setprecision(3) << m_LevelWindow.GetWindow(); level = ssLevel.str().c_str(); window = ssWindow.str().c_str(); } else { level.setNum((int)(m_LevelWindow.GetLevel())); window.setNum((int)(m_LevelWindow.GetWindow())); } m_LevelInput->setText(level); m_WindowInput->setText(window); m_LevelInput->setEnabled(!m_LevelWindow.IsFixed()); m_WindowInput->setEnabled(!m_LevelWindow.IsFixed()); this->show(); } catch (...) { try { this->hide(); } catch (...) { } } } -void QmitkLineEditLevelWindowWidget::setLevelWindowManager(mitk::LevelWindowManager *levelWindowManager) +void QmitkLineEditLevelWindowWidget::SetLevelWindowManager(mitk::LevelWindowManager *levelWindowManager) { if (m_IsObserverTagSet) { m_Manager->RemoveObserver(m_ObserverTag); m_IsObserverTagSet = false; } m_Manager = levelWindowManager; if (m_Manager.IsNotNull()) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmitkLineEditLevelWindowWidget::OnPropertyModified); m_ObserverTag = m_Manager->AddObserver(itk::ModifiedEvent(), command); m_IsObserverTagSet = true; } } void QmitkLineEditLevelWindowWidget::SetDataStorage(mitk::DataStorage *ds) { m_Manager->SetDataStorage(ds); } -// read the levelInput and change level and slider when the button "ENTER" was pressed in the windowInput-LineEdit void QmitkLineEditLevelWindowWidget::SetLevelValue() { double level = m_LevelInput->text().toDouble(); m_LevelWindow.SetLevelWindow(level, m_LevelWindow.GetWindow()); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } -// read the windowInput and change window and slider when the button "ENTER" was pressed in the windowInput-LineEdit void QmitkLineEditLevelWindowWidget::SetWindowValue() { double window = m_WindowInput->text().toDouble(); m_LevelWindow.SetLevelWindow(m_LevelWindow.GetLevel(), window); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLineEditLevelWindowWidget::contextMenuEvent(QContextMenuEvent *) { - m_Contextmenu->setLevelWindowManager(m_Manager.GetPointer()); - m_Contextmenu->getContextMenu(); + m_Contextmenu->SetLevelWindowManager(m_Manager.GetPointer()); + m_Contextmenu->GetContextMenu(); } mitk::LevelWindowManager *QmitkLineEditLevelWindowWidget::GetManager() { return m_Manager.GetPointer(); -} \ No newline at end of file +} diff --git a/Modules/QtWidgets/src/QmitkNodeDetailsDialog.cpp b/Modules/QtWidgets/src/QmitkNodeDetailsDialog.cpp index 7bf52a09ca..262d9c5053 100644 --- a/Modules/QtWidgets/src/QmitkNodeDetailsDialog.cpp +++ b/Modules/QtWidgets/src/QmitkNodeDetailsDialog.cpp @@ -1,127 +1,145 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkNodeDetailsDialog.h" #include "QmitkDataStorageComboBox.h" #include #include #include #include #include #include #include QmitkNodeDetailsDialog::QmitkNodeDetailsDialog(const QList& nodes, QWidget* parent /*= nullptr*/, Qt::WindowFlags flags /*= nullptr */) : QDialog(parent, flags) +{ + QList constNodes; + + for (auto& node : nodes) + { + constNodes.append(node.GetPointer()); + } + + InitWidgets(constNodes); +} + +QmitkNodeDetailsDialog::QmitkNodeDetailsDialog(const QList& nodes, QWidget* parent /*= nullptr*/, Qt::WindowFlags flags /*= nullptr */) + : QDialog(parent, flags) +{ + InitWidgets(nodes); +} + +void QmitkNodeDetailsDialog::InitWidgets(const QList& nodes) { auto parentLayout = new QGridLayout; auto dataStorageComboBox = new QmitkDataStorageComboBox(this, true); m_KeyWord = new QLineEdit; m_KeyWord->installEventFilter(this); m_SearchButton = new QPushButton("Search (F3)", this); m_SearchButton->installEventFilter(this); m_TextBrowser = new QTextBrowser(this); QPushButton* cancelButton = new QPushButton("Cancel", this); setMinimumSize(512, 512); setLayout(parentLayout); setSizeGripEnabled(true); setModal(true); parentLayout->addWidget(dataStorageComboBox, 0, 0, 1, 2); parentLayout->addWidget(m_KeyWord, 1, 0); parentLayout->addWidget(m_SearchButton, 1, 1); parentLayout->addWidget(m_TextBrowser, 2, 0, 1, 2); parentLayout->addWidget(cancelButton, 3, 0, 1, 2); connect(dataStorageComboBox, &QmitkDataStorageComboBox::OnSelectionChanged, this, &QmitkNodeDetailsDialog::OnSelectionChanged); - for(auto& node : nodes) + for (auto& node : nodes) { dataStorageComboBox->AddNode(node); } connect(m_KeyWord, &QLineEdit::textChanged, this, &QmitkNodeDetailsDialog::KeyWordTextChanged); connect(m_SearchButton, &QPushButton::clicked, this, &QmitkNodeDetailsDialog::OnSearchButtonClicked); connect(cancelButton, &QPushButton::clicked, this, &QmitkNodeDetailsDialog::OnCancelButtonClicked); cancelButton->setDefault(true); -} +}; void QmitkNodeDetailsDialog::OnSelectionChanged(const mitk::DataNode* node) { if (nullptr == node) { return; } std::ostringstream s; itk::Indent i(2); mitk::BaseData* baseData = node->GetData(); if (nullptr != baseData) { baseData->Print(s, i); } m_TextBrowser->setPlainText(QString::fromStdString(s.str())); } void QmitkNodeDetailsDialog::OnSearchButtonClicked(bool /*checked*/ /*= false */) { QString keyWord = m_KeyWord->text(); QString text = m_TextBrowser->toPlainText(); if (keyWord.isEmpty() || text.isEmpty()) { return; } m_TextBrowser->find(keyWord); m_SearchButton->setText("Search Next(F3)"); } void QmitkNodeDetailsDialog::OnCancelButtonClicked(bool /*checked*/ /*= false */) { done(0); } bool QmitkNodeDetailsDialog::eventFilter(QObject* obj, QEvent* event) { if (event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_F3 || keyEvent->key() == Qt::Key_Return) { // trigger deletion of selected node(s) OnSearchButtonClicked(true); // return true: this means the delete key event is not send to the table return true; } } // standard event processing return QObject::eventFilter(obj, event); } void QmitkNodeDetailsDialog::KeyWordTextChanged(const QString& /*text*/) { QTextCursor textCursor = m_TextBrowser->textCursor(); textCursor.setPosition(0); m_TextBrowser->setTextCursor(textCursor); m_SearchButton->setText("Search (F3)"); } diff --git a/Modules/QtWidgets/src/QmitkSliderLevelWindowWidget.cpp b/Modules/QtWidgets/src/QmitkSliderLevelWindowWidget.cpp index d491d9185c..1642b8c67b 100644 --- a/Modules/QtWidgets/src/QmitkSliderLevelWindowWidget.cpp +++ b/Modules/QtWidgets/src/QmitkSliderLevelWindowWidget.cpp @@ -1,570 +1,554 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include +// mitk core +#include + +// mitk qt widgets +#include + +// qt #include #include #include #include -#include +// itk #include -#include +// c++ #include -/** -* Constructor -*/ -QmitkSliderLevelWindowWidget::QmitkSliderLevelWindowWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) +QmitkSliderLevelWindowWidget::QmitkSliderLevelWindowWidget(QWidget *parent, Qt::WindowFlags f) + : QWidget(parent, f) { m_Manager = mitk::LevelWindowManager::New(); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSliderLevelWindowWidget::OnPropertyModified); m_ObserverTag = m_Manager->AddObserver(itk::ModifiedEvent(), command); m_IsObserverTagSet = true; setMouseTracking(true); m_Resize = false; m_Bottom = false; m_CtrlPressed = false; m_MouseDown = false; m_Font.setPointSize(6); m_MoveHeight = height() - 25; m_ScaleVisible = true; - m_Contextmenu = new QmitkLevelWindowWidgetContextMenu(this); //, true); - - // setBackgroundMode( Qt::NoBackground ); + m_Contextmenu = new QmitkLevelWindowWidgetContextMenu(this); this->hide(); - update(); + Update(); } QmitkSliderLevelWindowWidget::~QmitkSliderLevelWindowWidget() { if (m_IsObserverTagSet) { m_Manager->RemoveObserver(m_ObserverTag); m_IsObserverTagSet = false; } } -void QmitkSliderLevelWindowWidget::setLevelWindowManager(mitk::LevelWindowManager *levelWindowManager) +void QmitkSliderLevelWindowWidget::SetLevelWindowManager(mitk::LevelWindowManager *levelWindowManager) { if (m_IsObserverTagSet) { m_Manager->RemoveObserver(m_ObserverTag); m_IsObserverTagSet = false; } m_Manager = levelWindowManager; if (m_Manager.IsNotNull()) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSliderLevelWindowWidget::OnPropertyModified); m_ObserverTag = m_Manager->AddObserver(itk::ModifiedEvent(), command); m_IsObserverTagSet = true; } } void QmitkSliderLevelWindowWidget::OnPropertyModified(const itk::EventObject &) { try { m_LevelWindow = m_Manager->GetLevelWindow(); this->show(); - update(); + Update(); } catch (...) { try { this->hide(); } catch (...) { } } } void QmitkSliderLevelWindowWidget::paintEvent(QPaintEvent *itkNotUsed(e)) { QPixmap pm(width(), height()); pm.fill(this->palette().color(this->backgroundRole())); QPainter painter(&pm); painter.setFont(m_Font); painter.setPen(this->palette().color(this->foregroundRole())); QColor c(51, 153, 204); QColor cl = c.light(); QColor cd = c.dark(); painter.setBrush(c); painter.drawRect(m_Rect); float mr = m_LevelWindow.GetRange(); float smallestLevelableValue = 1e-9; //This check is needed as safe guard. LevelWindow is refactored to only deduce finite ranges //from images, but old scene serialization may contain infinite ranges that overwrite the new //business logic. This roots in two many "jobs" LevelWindow" is used for; see also T24962. //Until LevelWindow and this widget is refactored the check was the minimal invasive fix. if (!std::isfinite(mr)) { mr = m_LevelWindow.GetWindow(); } // avoiding a division by 0 while still enabling small level windows if (mr < smallestLevelableValue) mr = smallestLevelableValue; float fact = (float)m_MoveHeight / mr; // begin draw scale if (m_ScaleVisible) { double minRange = (double)m_LevelWindow.GetRangeMin(); double maxRange = (double)m_LevelWindow.GetRangeMax(); //This check is needed as safe guard. LevelWindow is refactored to only deduce finite ranges //from images, but old scene serialization may contain infinite ranges that overwrite the new //business logic. This roots in two many "jobs" LevelWindow" is used for; see also T24962. //Until LevelWindow and this widget is refactored the check was the minimal invasive fix. if (!std::isfinite(minRange)) { minRange = m_LevelWindow.GetLowerWindowBound(); } //This check is needed as safe guard. LevelWindow is refactored to only deduce finite ranges //from images, but old scene serialization may contain infinite ranges that overwrite the new //business logic. This roots in two many "jobs" LevelWindow" is used for; see also T24962. //Until LevelWindow and this widget is refactored the check was the minimal invasive fix. if (!std::isfinite(maxRange)) { maxRange = m_LevelWindow.GetUpperWindowBound(); } int yValue = m_MoveHeight + (int)(minRange * fact); QString s = " 0"; if (minRange < 0 && maxRange > 0) { painter.drawLine(5, yValue, 15, yValue); painter.drawText(21, yValue + 3, s); } int count = 1; int k = 5; bool enoughSpace = false; bool enoughSpace2 = false; double dStepSize = pow(10, floor(log10(mr / 100)) + 1); for (int i = m_MoveHeight + (int)(minRange * fact); i < m_MoveHeight;) // negative { if (-count * dStepSize < minRange) { break; } yValue = m_MoveHeight + (int)((minRange + count * dStepSize) * fact); s = QString::number(-count * dStepSize); if (count % k && ((dStepSize * fact) > 2.5)) { painter.drawLine(8, yValue, 12, yValue); enoughSpace = true; } else if (!(count % k)) { if ((k * dStepSize * fact) > 7) { painter.drawLine(5, yValue, 15, yValue); painter.drawText(21, yValue + 3, s); enoughSpace2 = true; } else { k += 5; } } if (enoughSpace) { i = yValue; count++; } else if (enoughSpace2) { i = yValue; count += k; } else { i = yValue; count = k; } } count = 1; k = 5; enoughSpace = false; enoughSpace2 = false; for (int i = m_MoveHeight + (int)(minRange * fact); i >= 0;) { if (count * dStepSize > maxRange) { break; } yValue = m_MoveHeight + (int)((minRange - count * dStepSize) * fact); s = QString::number(count * dStepSize); if (count % k && ((dStepSize * fact) > 2.5)) { if (!(minRange > 0 && (count * dStepSize) < minRange)) painter.drawLine(8, yValue, 12, yValue); enoughSpace = true; } else if (!(count % k)) { if ((k * dStepSize * fact) > 7) { if (!(minRange > 0 && (count * dStepSize) < minRange)) { painter.drawLine(5, yValue, 15, yValue); painter.drawText(21, yValue + 3, s); } enoughSpace2 = true; } else { k += 5; } } if (enoughSpace) { i = yValue; count++; } else if (enoughSpace2) { i = yValue; count += k; } else { i = yValue; count = k; } } } // end draw scale painter.setPen(cl); painter.drawLine(m_Rect.topLeft(), m_Rect.topRight()); painter.drawLine(m_Rect.topLeft(), m_Rect.bottomLeft()); painter.setPen(cd); painter.drawLine(m_Rect.topRight(), m_Rect.bottomRight()); painter.drawLine(m_Rect.bottomRight(), m_Rect.bottomLeft()); painter.end(); QPainter p(this); p.drawPixmap(0, 0, pm); } -/** -* -*/ void QmitkSliderLevelWindowWidget::mouseMoveEvent(QMouseEvent *mouseEvent) { if (!mouseEvent) return; if (m_LevelWindow.IsFixed()) return; if (!m_MouseDown) { if (mouseEvent->pos().y() >= 0 && mouseEvent->pos().y() <= (m_Rect.topLeft().y() + 3)) { setCursor(Qt::SizeVerCursor); m_UpperBound.setRect(m_Rect.topLeft().x(), m_Rect.topLeft().y() - 3, 17, 7); this->setToolTip("Ctrl + left click to change only upper bound"); m_Resize = true; } else if (mouseEvent->pos().y() >= (m_Rect.bottomLeft().y() - 3)) { setCursor(Qt::SizeVerCursor); m_LowerBound.setRect(m_Rect.bottomLeft().x(), m_Rect.bottomLeft().y() - 3, 17, 7); this->setToolTip("Ctrl + left click to change only lower bound"); m_Resize = true; m_Bottom = true; } else { setCursor(Qt::ArrowCursor); this->setToolTip("Left click and mouse move to adjust the slider"); m_Resize = false; m_Bottom = false; } } else { float fact = (float)m_MoveHeight / m_LevelWindow.GetRange(); if (m_Leftbutton) { if (m_Resize && !m_CtrlPressed) { double diff = (mouseEvent->pos().y()) / fact; diff -= (m_StartPos.y()) / fact; m_StartPos = mouseEvent->pos(); if (diff == 0) return; float value; if (m_Bottom) value = m_LevelWindow.GetWindow() + ((2 * diff)); else value = m_LevelWindow.GetWindow() - ((2 * diff)); if (value < 0) value = 0; m_LevelWindow.SetLevelWindow(m_LevelWindow.GetLevel(), value); } else if (m_Resize && m_CtrlPressed) { if (!m_Bottom) { double diff = (mouseEvent->pos().y()) / fact; diff -= (m_StartPos.y()) / fact; m_StartPos = mouseEvent->pos(); if (diff == 0) return; float value; value = m_LevelWindow.GetWindow() - ((diff)); if (value < 0) value = 0; float oldWindow; float oldLevel; float newLevel; oldWindow = m_LevelWindow.GetWindow(); oldLevel = m_LevelWindow.GetLevel(); newLevel = oldLevel + (value - oldWindow) / 2; if (!((newLevel + value / 2) > m_LevelWindow.GetRangeMax())) m_LevelWindow.SetLevelWindow(newLevel, value); } else { double diff = (mouseEvent->pos().y()) / fact; diff -= (m_StartPos.y()) / fact; m_StartPos = mouseEvent->pos(); if (diff == 0) return; float value; value = m_LevelWindow.GetWindow() + ((diff)); if (value < 0) value = 0; float oldWindow; float oldLevel; float newLevel; oldWindow = m_LevelWindow.GetWindow(); oldLevel = m_LevelWindow.GetLevel(); newLevel = oldLevel - (value - oldWindow) / 2; if (!((newLevel - value / 2) < m_LevelWindow.GetRangeMin())) m_LevelWindow.SetLevelWindow(newLevel, value); } } else { const float minv = m_LevelWindow.GetRangeMin(); const float level = (m_MoveHeight - mouseEvent->pos().y()) / fact + minv; double diff = (mouseEvent->pos().x()) / fact; diff -= (m_StartPos.x()) / fact; m_StartPos = mouseEvent->pos(); float window; if (m_Bottom) window = m_LevelWindow.GetWindow() + ((2 * diff)); else window = m_LevelWindow.GetWindow() - ((2 * diff)); if (window < 0) window = 0; m_LevelWindow.SetLevelWindow(level, window); } m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } + void QmitkSliderLevelWindowWidget::enterEvent(QEvent * /*event*/) { - /* - if(event->type() != QEvent::MouseMove) - return;*/ - - // mouseMoveEvent( static_cast< QMouseEvent* > ( event ) ); QPoint p = QCursor::pos(); p = this->mapFromGlobal(p); QMouseEvent ev(QEvent::MouseMove, p, Qt::NoButton, Qt::NoButton, Qt::NoModifier); this->mouseMoveEvent(&ev); } -/** -* -*/ void QmitkSliderLevelWindowWidget::mousePressEvent(QMouseEvent *mouseEvent) { if (m_LevelWindow.IsFixed()) return; m_MouseDown = true; m_StartPos = mouseEvent->pos(); if (mouseEvent->button() == Qt::LeftButton) { if (mouseEvent->modifiers() == Qt::ControlModifier || mouseEvent->modifiers() == Qt::ShiftModifier) { m_CtrlPressed = true; } else { m_CtrlPressed = false; } m_Leftbutton = true; } else m_Leftbutton = false; mouseMoveEvent(mouseEvent); } -/** -* -*/ void QmitkSliderLevelWindowWidget::resizeEvent(QResizeEvent *event) { m_MoveHeight = event->size().height() - 25; - update(); + Update(); } -/** -* -*/ void QmitkSliderLevelWindowWidget::mouseReleaseEvent(QMouseEvent *) { if (m_LevelWindow.IsFixed()) return; m_MouseDown = false; } -/** -* -*/ -void QmitkSliderLevelWindowWidget::update() +void QmitkSliderLevelWindowWidget::Update() { int rectWidth; if (m_ScaleVisible) { rectWidth = 17; setMinimumSize(QSize(50, 50)); setMaximumSize(QSize(50, 2000)); setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); } else { rectWidth = 26; setMinimumSize(QSize(40, 50)); setMaximumSize(QSize(50, 2000)); setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); } float mr = m_LevelWindow.GetRange(); if (mr < 1e-9) mr = 1e-9; float fact = (float)m_MoveHeight / mr; float rectHeight = m_LevelWindow.GetWindow() * fact; if (rectHeight < 15) rectHeight = 15; if (m_LevelWindow.GetLowerWindowBound() < 0) m_Rect.setRect(2, (int)(m_MoveHeight - (m_LevelWindow.GetUpperWindowBound() - m_LevelWindow.GetRangeMin()) * fact), rectWidth, (int)rectHeight); else m_Rect.setRect(2, (int)(m_MoveHeight - (m_LevelWindow.GetUpperWindowBound() - m_LevelWindow.GetRangeMin()) * fact), rectWidth, (int)rectHeight); QWidget::repaint(); } void QmitkSliderLevelWindowWidget::contextMenuEvent(QContextMenuEvent *) { - m_Contextmenu->setLevelWindowManager(m_Manager.GetPointer()); + m_Contextmenu->SetLevelWindowManager(m_Manager.GetPointer()); auto contextMenu = new QMenu(this); Q_CHECK_PTR(contextMenu); if (m_ScaleVisible) - contextMenu->addAction(tr("Hide Scale"), this, SLOT(hideScale())); + contextMenu->addAction(tr("Hide Scale"), this, SLOT(HideScale())); else - contextMenu->addAction(tr("Show Scale"), this, SLOT(showScale())); + contextMenu->addAction(tr("Show Scale"), this, SLOT(ShowScale())); contextMenu->addSeparator(); - m_Contextmenu->getContextMenu(contextMenu); + m_Contextmenu->GetContextMenu(contextMenu); // Fix: Bug #13327 we need to reset the m_MouseDown value // otherwise the cursor is not correctly restored afterwards m_MouseDown = false; } -void QmitkSliderLevelWindowWidget::hideScale() +void QmitkSliderLevelWindowWidget::HideScale() { m_ScaleVisible = false; - update(); + Update(); } -void QmitkSliderLevelWindowWidget::showScale() +void QmitkSliderLevelWindowWidget::ShowScale() { m_ScaleVisible = true; - update(); + Update(); } -void QmitkSliderLevelWindowWidget::setDataStorage(mitk::DataStorage *ds) +void QmitkSliderLevelWindowWidget::SetDataStorage(mitk::DataStorage *ds) { m_Manager->SetDataStorage(ds); } mitk::LevelWindowManager *QmitkSliderLevelWindowWidget::GetManager() { return m_Manager.GetPointer(); } diff --git a/Modules/REST/test/mitkRESTClientTest.cpp b/Modules/REST/test/mitkRESTClientTest.cpp index 1fe3dbdaab..9c4f7c0123 100644 --- a/Modules/REST/test/mitkRESTClientTest.cpp +++ b/Modules/REST/test/mitkRESTClientTest.cpp @@ -1,247 +1,247 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include class mitkRESTClientTestSuite : public mitk::TestFixture, mitk::IRESTObserver { CPPUNIT_TEST_SUITE(mitkRESTClientTestSuite); // MITK_TEST(GetRequestValidURI_ReturnsExpectedJSON); GET requests do not support content yet? MITK_TEST(MultipleGetRequestValidURI_AllTasksFinish); - MITK_TEST(PutRequestValidURI_ReturnsExpectedJSON); - MITK_TEST(PostRequestValidURI_ReturnsExpectedJSON); + // MITK_TEST(PutRequestValidURI_ReturnsExpectedJSON); Does not work reliably on dart clients + // MITK_TEST(PostRequestValidURI_ReturnsExpectedJSON); -- " -- MITK_TEST(GetRequestInvalidURI_ThrowsException); MITK_TEST(PutRequestInvalidURI_ThrowsException); MITK_TEST(PostRequestInvalidURI_ThrowsException); CPPUNIT_TEST_SUITE_END(); public: mitk::IRESTManager *m_Service; web::json::value m_Data; web::json::value Notify(const web::uri &, const web::json::value &) override { return m_Data; } /** * @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_Data = web::json::value(); m_Data[_XPLATSTR("userId")] = web::json::value(1); m_Data[_XPLATSTR("id")] = web::json::value(1); m_Data[_XPLATSTR("title")] = web::json::value(U("this is a title")); m_Data[_XPLATSTR("body")] = web::json::value(U("this is a body")); us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); if (serviceRef) { m_Service = us::GetModuleContext()->GetService(serviceRef); } - + if (!m_Service) { CPPUNIT_FAIL("Getting Service in setUp() failed"); } - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/test"), this); + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/clienttest"), this); } void tearDown() override { m_Service->HandleDeleteObserver(this); } void GetRequestValidURI_ReturnsExpectedJSON() { web::json::value result; - m_Service->SendRequest(_XPLATSTR("http://localhost:8080/test")) + m_Service->SendRequest(_XPLATSTR("http://localhost:8080/clienttest")) .then([&](pplx::task resultTask) { try { result = resultTask.get(); } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }) .wait(); - + CPPUNIT_ASSERT_MESSAGE("Result is the expected JSON value", result == m_Data); } void MultipleGetRequestValidURI_AllTasksFinish() { int count = 0; // Create multiple tasks e.g. as shown below std::vector> tasks; for (int i = 0; i < 20; ++i) { pplx::task singleTask = - m_Service->SendRequest(_XPLATSTR("http://localhost:8080/test")) + m_Service->SendRequest(_XPLATSTR("http://localhost:8080/clienttest")) .then([&](pplx::task resultTask) { // Do something when a single task is done try { resultTask.get(); count +=1; } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }); tasks.emplace_back(singleTask); } // Create a joinTask which includes all tasks you've created auto joinTask = pplx::when_all(begin(tasks), end(tasks)); // Run asynchonously joinTask.then([&](pplx::task resultTask) { // Do something when all tasks are finished try { resultTask.get(); count += 1; } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }).wait(); - + CPPUNIT_ASSERT_MESSAGE("Multiple Requests", 21 == count); } void PutRequestValidURI_ReturnsExpectedJSON() { // optional: link might get invalid or content is changed web::json::value result; m_Service ->SendRequest(_XPLATSTR("https://jsonplaceholder.typicode.com/posts/1"), mitk::IRESTManager::RequestType::Put, &m_Data) .then([&](pplx::task resultTask) { try { result = resultTask.get(); } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }) .wait(); - + CPPUNIT_ASSERT_MESSAGE( "Result is the expected JSON value, check if the link is still valid since this is an optional test", result == m_Data); } void PostRequestValidURI_ReturnsExpectedJSON() { // optional: link might get invalid or content is changed web::json::value result; web::json::value data; data[_XPLATSTR("userId")] = m_Data[_XPLATSTR("userId")]; data[_XPLATSTR("title")] = m_Data[_XPLATSTR("title")]; data[_XPLATSTR("body")] = m_Data[_XPLATSTR("body")]; m_Service->SendRequest(_XPLATSTR("https://jsonplaceholder.typicode.com/posts"), mitk::IRESTManager::RequestType::Post, &data) .then([&](pplx::task resultTask) { try { result = resultTask.get(); } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }) .wait(); - + data[_XPLATSTR("id")] = web::json::value(101); CPPUNIT_ASSERT_MESSAGE( "Result is the expected JSON value, check if the link is still valid since this is an optional test", result == data); } void GetException() { //Method which makes a get request to an invalid uri web::json::value result; m_Service->SendRequest(_XPLATSTR("http://localhost:1234/invalid")) .then([&](pplx::task resultTask) { result = resultTask.get(); }) .wait(); } void GetRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(GetException(), mitk::Exception); } - void PutException() + void PutException() { //Method which makes a put request to an invalid uri web::json::value result; m_Service->SendRequest(_XPLATSTR("http://localhost:1234/invalid"), mitk::IRESTManager::RequestType::Put, &m_Data) .then([&](pplx::task resultTask) { result = resultTask.get();}) - .wait(); + .wait(); } void PutRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(PutException(), mitk::Exception); } void PostException() { //Method which makes a post request to an invalid uri web::json::value result; m_Service->SendRequest(_XPLATSTR("http://localhost:1234/invalid"), mitk::IRESTManager::RequestType::Post, &m_Data) .then([&](pplx::task resultTask) { result = resultTask.get(); }) .wait(); } void PostRequestInvalidURI_ThrowsException() { CPPUNIT_ASSERT_THROW(PostException(), mitk::Exception); } }; MITK_TEST_SUITE_REGISTRATION(mitkRESTClient) diff --git a/Modules/REST/test/mitkRESTServerTest.cpp b/Modules/REST/test/mitkRESTServerTest.cpp index 86f7fa3057..a386dba712 100644 --- a/Modules/REST/test/mitkRESTServerTest.cpp +++ b/Modules/REST/test/mitkRESTServerTest.cpp @@ -1,227 +1,227 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifdef _WIN32 #include #endif #include #include #include #include #include #include #include #include #include class mitkRESTServerTestSuite : public mitk::TestFixture, mitk::IRESTObserver { CPPUNIT_TEST_SUITE(mitkRESTServerTestSuite); MITK_TEST(OpenListener_Succeed); MITK_TEST(TwoListenerSameHostSamePort_OnlyOneOpened); MITK_TEST(CloseListener_Succeed); MITK_TEST(OpenMultipleListenerCloseOne_Succeed); MITK_TEST(OpenMultipleListenerCloseAll_Succeed); // MITK_TEST(OpenListenerGetRequestSamePath_ReturnExpectedJSON); GET requests do not support content yet? MITK_TEST(CloseListener_NoRequestPossible); MITK_TEST(OpenListenerGetRequestDifferentPath_ReturnNotFound); MITK_TEST(OpenListenerCloseAndReopen_Succeed); CPPUNIT_TEST_SUITE_END(); public: mitk::IRESTManager *m_Service; web::json::value m_Data; web::json::value Notify(const web::uri &, const web::json::value &) override { return m_Data; } /** * @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_Data = web::json::value(); m_Data[_XPLATSTR("userId")] = web::json::value(1); m_Data[_XPLATSTR("id")] = web::json::value(1); m_Data[_XPLATSTR("title")] = web::json::value(U("this is a title")); m_Data[_XPLATSTR("body")] = web::json::value(U("this is a body")); auto serviceRef = us::GetModuleContext()->GetServiceReference(); if (serviceRef) m_Service = us::GetModuleContext()->GetService(serviceRef); if (!m_Service) CPPUNIT_FAIL("Getting Service in setUp() failed"); } void tearDown() override { m_Service->HandleDeleteObserver(this); } void OpenListener_Succeed() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/test"), this); + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); } void TwoListenerSameHostSamePort_OnlyOneOpened() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/test"), this); - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/example"), this); - + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/serverexample"), this); + CPPUNIT_ASSERT_MESSAGE("Open two listener with a different path,same host, same port, observer map size is two", 2 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open two listener with a different path,same host, same port, server map size is one", 1 == m_Service->GetServerMap().size()); } void CloseListener_Succeed() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/test"), this); - + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); m_Service->HandleDeleteObserver(this); - + CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size()); } void OpenMultipleListenerCloseOne_Succeed() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/test"), this); - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8090/example"), this); - + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8090/serverexample"), this); + CPPUNIT_ASSERT_MESSAGE("Open two listener, observer map size is two", 2 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open two listener, server map size is two", 2 == m_Service->GetServerMap().size()); - m_Service->HandleDeleteObserver(this, _XPLATSTR("http://localhost:8080/test")); - + m_Service->HandleDeleteObserver(this, _XPLATSTR("http://localhost:8080/servertest")); + CPPUNIT_ASSERT_MESSAGE("Closed one of two listeners, observer map is size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Closed one of two listener, server map size is one", 1 == m_Service->GetServerMap().size()); } void OpenMultipleListenerCloseAll_Succeed() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/test"), this); - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8090/example"), this); - + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8090/serverexample"), this); + CPPUNIT_ASSERT_MESSAGE("Open two listener, observer map size is two", 2 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open two listener, server map size is two", 2 == m_Service->GetServerMap().size()); m_Service->HandleDeleteObserver(this); - + CPPUNIT_ASSERT_MESSAGE("Closed all listeners, observer map is empty", 0 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Closed all listeners, server map is empty", 0 == m_Service->GetServerMap().size()); } void OpenListenerGetRequestSamePath_ReturnExpectedJSON() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/test"), this); - + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + web::json::value result; - m_Service->SendRequest(_XPLATSTR("http://localhost:8080/test")) + m_Service->SendRequest(_XPLATSTR("http://localhost:8080/servertest")) .then([&](pplx::task resultTask) { try { result = resultTask.get(); } catch (const mitk::Exception &exception) { MITK_ERROR << exception.what(); return; } }) .wait(); - + CPPUNIT_ASSERT_MESSAGE("Opened listener and send request to same uri, returned expected JSON", result == m_Data); } void RequestToClosedListener() { web::json::value result; - m_Service->SendRequest(_XPLATSTR("http://localhost:8080/test")) + m_Service->SendRequest(_XPLATSTR("http://localhost:8080/servertest")) .then([&](pplx::task resultTask) { result = resultTask.get(); }) .wait(); } void CloseListener_NoRequestPossible() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/test"), this); - + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); m_Service->HandleDeleteObserver(this); - + CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size()); - + CPPUNIT_ASSERT_THROW(RequestToClosedListener(), mitk::Exception); } void RequestToDifferentPathNotFound() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/test"), this); - + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + web::json::value result; - m_Service->SendRequest(_XPLATSTR("http://localhost:8080/example")) + m_Service->SendRequest(_XPLATSTR("http://localhost:8080/serverexample")) .then([&](pplx::task resultTask) { result = resultTask.get(); }) - .wait(); + .wait(); } void OpenListenerGetRequestDifferentPath_ReturnNotFound() { CPPUNIT_ASSERT_THROW(RequestToDifferentPathNotFound(), mitk::Exception); } - void OpenListenerCloseAndReopen_Succeed() + void OpenListenerCloseAndReopen_Succeed() { - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/test"), this); - + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + CPPUNIT_ASSERT_MESSAGE("Open one listener, observer map size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Open one listener, server map size is one", 1 == m_Service->GetServerMap().size()); m_Service->HandleDeleteObserver(this); - + CPPUNIT_ASSERT_MESSAGE("Closed listener, observer map is empty", 0 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Closed listener, server map is empty", 0 == m_Service->GetServerMap().size()); - m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/test"), this); - + m_Service->ReceiveRequest(_XPLATSTR("http://localhost:8080/servertest"), this); + CPPUNIT_ASSERT_MESSAGE("Reopened listener, observer map size is one", 1 == m_Service->GetObservers().size()); CPPUNIT_ASSERT_MESSAGE("Reopened listener, server map size is one", 1 == m_Service->GetServerMap().size()); } }; MITK_TEST_SUITE_REGISTRATION(mitkRESTServer) diff --git a/Plugins/PluginList.cmake b/Plugins/PluginList.cmake index eb8afbc120..750fbe68c8 100644 --- a/Plugins/PluginList.cmake +++ b/Plugins/PluginList.cmake @@ -1,117 +1,118 @@ # Plug-ins must be ordered according to their dependencies set(MITK_PLUGINS org.blueberry.core.runtime:ON org.blueberry.core.expressions:OFF org.blueberry.core.commands:OFF org.blueberry.core.jobs:OFF org.blueberry.ui.qt:OFF org.blueberry.ui.qt.help:ON org.blueberry.ui.qt.log:ON org.blueberry.ui.qt.objectinspector:OFF #org.blueberry.test:ON #org.blueberry.uitest:ON #Testing/org.blueberry.core.runtime.tests:ON #Testing/org.blueberry.osgi.tests:ON org.mitk.core.services:ON org.mitk.gui.common:ON org.mitk.planarfigure:ON org.mitk.core.ext:OFF org.mitk.core.jobs:OFF org.mitk.gui.qt.application:ON org.mitk.gui.qt.coreapplication:OFF org.mitk.gui.qt.ext:OFF org.mitk.gui.qt.extapplication:OFF org.mitk.gui.qt.common:ON org.mitk.gui.qt.stdmultiwidgeteditor:ON org.mitk.gui.qt.mxnmultiwidgeteditor:OFF org.mitk.gui.qt.common.legacy:OFF org.mitk.gui.qt.cmdlinemodules:OFF org.mitk.gui.qt.chartExample:OFF org.mitk.gui.qt.diffusionimagingapp:OFF org.mitk.gui.qt.datamanager:ON org.mitk.gui.qt.datamanagerlight:OFF org.mitk.gui.qt.datastorageviewertest:OFF org.mitk.gui.qt.properties:ON org.mitk.gui.qt.basicimageprocessing:OFF + org.mitk.gui.qt.bonesegmentation:OFF org.mitk.gui.qt.dicom:OFF org.mitk.gui.qt.dicominspector:OFF org.mitk.gui.qt.diffusionimaging:OFF org.mitk.gui.qt.diffusionimaging.connectomics:OFF org.mitk.gui.qt.diffusionimaging.denoising:OFF org.mitk.gui.qt.diffusionimaging.fiberfox:OFF org.mitk.gui.qt.diffusionimaging.fiberprocessing:OFF org.mitk.gui.qt.diffusionimaging.ivim:OFF org.mitk.gui.qt.diffusionimaging.odfpeaks:OFF org.mitk.gui.qt.diffusionimaging.partialvolume:OFF org.mitk.gui.qt.diffusionimaging.preprocessing:OFF org.mitk.gui.qt.diffusionimaging.reconstruction:OFF org.mitk.gui.qt.diffusionimaging.registration:OFF org.mitk.gui.qt.diffusionimaging.tractography:OFF org.mitk.gui.qt.diffusionimaging.python:OFF org.mitk.gui.qt.dosevisualization:OFF org.mitk.gui.qt.geometrytools:OFF org.mitk.gui.qt.igtexamples:OFF org.mitk.gui.qt.igttracking:OFF org.mitk.gui.qt.lasercontrol:OFF org.mitk.gui.qt.openigtlink:OFF org.mitk.gui.qt.imagecropper:OFF org.mitk.gui.qt.imagenavigator:ON org.mitk.gui.qt.viewnavigator:OFF org.mitk.gui.qt.materialeditor:OFF org.mitk.gui.qt.measurementtoolbox:OFF org.mitk.gui.qt.moviemaker:OFF org.mitk.gui.qt.pointsetinteraction:OFF org.mitk.gui.qt.pointsetinteractionmultispectrum:OFF org.mitk.gui.qt.python:OFF org.mitk.gui.qt.remeshing:OFF org.mitk.gui.qt.segmentation:OFF org.mitk.gui.qt.aicpregistration:OFF org.mitk.gui.qt.renderwindowmanager:OFF org.mitk.gui.qt.semanticrelations:OFF org.mitk.gui.qt.toftutorial:OFF org.mitk.gui.qt.tofutil:OFF org.mitk.gui.qt.tubegraph:OFF org.mitk.gui.qt.ugvisualization:OFF org.mitk.gui.qt.photoacoustics.pausviewer:OFF org.mitk.gui.qt.photoacoustics.pausmotioncompensation:OFF org.mitk.gui.qt.photoacoustics.imageprocessing:OFF org.mitk.gui.qt.photoacoustics.simulation:OFF org.mitk.gui.qt.photoacoustics.spectralunmixing:OFF org.mitk.gui.qt.ultrasound:OFF org.mitk.gui.qt.volumevisualization:OFF org.mitk.gui.qt.eventrecorder:OFF org.mitk.gui.qt.xnat:OFF org.mitk.gui.qt.igt.app.echotrack:OFF org.mitk.gui.qt.spectrocamrecorder:OFF org.mitk.gui.qt.classificationsegmentation:OFF org.mitk.gui.qt.overlaymanager:OFF org.mitk.gui.qt.igt.app.hummelprotocolmeasurements:OFF org.mitk.gui.qt.multilabelsegmentation:OFF org.mitk.matchpoint.core.helper:OFF org.mitk.gui.qt.matchpoint.algorithm.browser:OFF org.mitk.gui.qt.matchpoint.algorithm.control:OFF org.mitk.gui.qt.matchpoint.algorithm.batch:OFF org.mitk.gui.qt.matchpoint.mapper:OFF org.mitk.gui.qt.matchpoint.framereg:OFF org.mitk.gui.qt.matchpoint.visualizer:OFF org.mitk.gui.qt.matchpoint.evaluator:OFF org.mitk.gui.qt.matchpoint.manipulator:OFF org.mitk.gui.qt.preprocessing.resampling:OFF org.mitk.gui.qt.radiomics:OFF org.mitk.gui.qt.cest:OFF org.mitk.gui.qt.fit.demo:OFF org.mitk.gui.qt.fit.inspector:OFF org.mitk.gui.qt.fit.genericfitting:OFF org.mitk.gui.qt.pharmacokinetics.mri:OFF org.mitk.gui.qt.pharmacokinetics.pet:OFF org.mitk.gui.qt.pharmacokinetics.simulation:OFF org.mitk.gui.qt.pharmacokinetics.curvedescriptor:OFF org.mitk.gui.qt.pharmacokinetics.concentration.mri:OFF org.mitk.gui.qt.regiongrowing:OFF ) diff --git a/Plugins/org.blueberry.ui.qt/CMakeLists.txt b/Plugins/org.blueberry.ui.qt/CMakeLists.txt index 9995035799..a03b353eda 100644 --- a/Plugins/org.blueberry.ui.qt/CMakeLists.txt +++ b/Plugins/org.blueberry.ui.qt/CMakeLists.txt @@ -1,24 +1,24 @@ project(org_blueberry_ui_qt) set(PLUGIN_exported_include_suffixes src src/actions src/application src/commands src/guitk src/handlers src/intro src/model src/presentations src/services src/testing src/tweaklets src/util ) mitk_create_plugin(EXPORT_DIRECTIVE BERRY_UI_QT EXPORTED_INCLUDE_SUFFIXES ${PLUGIN_exported_include_suffixes}) if(MITK_USE_Qt5) - target_link_libraries(${PLUGIN_TARGET} PUBLIC Qt5::Widgets) + target_link_libraries(${PLUGIN_TARGET} PUBLIC Qt5::Widgets Qt5::PrintSupport Qt5::Svg) endif() diff --git a/Plugins/org.blueberry.ui.qt/src/berryWorkbenchPlugin.cpp b/Plugins/org.blueberry.ui.qt/src/berryWorkbenchPlugin.cpp index fc2560d196..356b919020 100644 --- a/Plugins/org.blueberry.ui.qt/src/berryWorkbenchPlugin.cpp +++ b/Plugins/org.blueberry.ui.qt/src/berryWorkbenchPlugin.cpp @@ -1,467 +1,477 @@ /*=================================================================== BlueBerry Platform Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "berryLog.h" #include "berryWorkbenchPlugin.h" #include "internal/berryWorkbenchRegistryConstants.h" #include "internal/berryWorkbench.h" #include "berryPlatform.h" #include "internal/intro/berryEditorIntroAdapterPart.h" #include "internal/defaultpresentation/berryQtWorkbenchPresentationFactory.h" #include "berryQtStyleManager.h" #include "berryExtensionFactory.h" #include "internal/berryQtWorkbenchTweaklet.h" #include "internal/berryQtWorkbenchPageTweaklet.h" #include "internal/berryQtWidgetsTweaklet.h" #include "internal/dialogs/berryPerspectivesPreferencePage.h" #include "internal/berryQtStylePreferencePage.h" #include "internal/berryStatusUtil.h" #include "internal/berryHandlerServiceFactory.h" #include "internal/berryMenuServiceFactory.h" #include "internal/berryCommandServiceFactory.h" #include "internal/berryWorkbenchSourceProvider.h" #include "berryObjectString.h" #include "berryObjects.h" #include "internal/berryPolicy.h" #include "internal/berryHandlerAuthority.h" #include "internal/berryOpenPerspectivePropertyTester.h" #include "internal/berryPerspectiveParameterValues.h" #include "internal/handlers/berryCloseAllPerspectivesHandler.h" #include "internal/handlers/berryClosePerspectiveHandler.h" #include "internal/handlers/berryDynamicHelpHandler.h" #include "internal/handlers/berryHelpContentsHandler.h" #include "internal/handlers/berryIntroHandler.h" #include "internal/handlers/berryOpenInNewWindowHandler.h" #include "internal/handlers/berryNewEditorHandler.h" #include "internal/handlers/berryQuitHandler.h" #include "internal/handlers/berryResetPerspectiveHandler.h" #include "internal/handlers/berrySavePerspectiveHandler.h" #include "internal/handlers/berryShowPerspectiveHandler.h" #include "internal/handlers/berryShowViewHandler.h" #include "berryIQtStyleManager.h" #include "berryIContributor.h" #include "berryILog.h" #include "berryIElementFactory.h" #include "berryIExtension.h" #include +#include +#include namespace berry { bool WorkbenchPlugin::DEBUG = false; char WorkbenchPlugin::PREFERENCE_PAGE_CATEGORY_SEPARATOR = '/'; WorkbenchPlugin* WorkbenchPlugin::inst = nullptr; WorkbenchPlugin::WorkbenchPlugin() : AbstractUICTKPlugin() { inst = this; presentationFactory = nullptr; editorRegistry = nullptr; viewRegistry = nullptr; perspRegistry = nullptr; introRegistry = nullptr; } WorkbenchPlugin::~WorkbenchPlugin() { delete presentationFactory; delete editorRegistry; delete viewRegistry; delete perspRegistry; delete introRegistry; inst = nullptr; } bool WorkbenchPlugin::HasExecutableExtension( const IConfigurationElement::Pointer& element, const QString& extensionName) { if (!element->GetAttribute(extensionName).isNull()) return true; QString elementText = element->GetValue(); if (!elementText.isEmpty()) return true; QList children(element->GetChildren(extensionName)); if (children.size() == 1) { if (!(children[0]->GetAttribute(WorkbenchRegistryConstants::ATT_CLASS).isNull())) return true; } return false; } bool WorkbenchPlugin::IsBundleLoadedForExecutableExtension( const IConfigurationElement::Pointer& element, const QString& extensionName) { QSharedPointer plugin = WorkbenchPlugin::GetBundleForExecutableExtension(element, extensionName); if (plugin.isNull()) return true; return plugin->getState() == ctkPlugin::ACTIVE; } QSharedPointer WorkbenchPlugin::GetBundleForExecutableExtension( const IConfigurationElement::Pointer& element, const QString& extensionName) { // this code is derived heavily from // ConfigurationElement.createExecutableExtension. QString prop; QString executable; QString contributorName; int i = 0; if (!extensionName.isNull()) prop = element->GetAttribute(extensionName); else { // property not specified, try as element value prop = element->GetValue(); if (!prop.isNull()) { prop = prop.trimmed(); if (prop.isEmpty()) prop = QString(); } } if (prop.isNull()) { // property not defined, try as a child element QList exec(element->GetChildren(extensionName)); if (!exec.isEmpty()) contributorName = exec[0]->GetAttribute("plugin"); } else { // simple property or element value, parse it into its components i = prop.indexOf(':'); if (i != -1) executable = prop.left(i).trimmed(); else executable = prop; i = executable.indexOf('/'); if (i != -1) contributorName = executable.left(i).trimmed(); } if (contributorName.isNull()) contributorName = element->GetContributor()->GetName(); return Platform::GetPlugin(contributorName); } WorkbenchPlugin* WorkbenchPlugin::GetDefault() { return inst; } std::size_t WorkbenchPlugin::GetBundleCount() { // TODO BundleContext GetBundles //return bundleContext->GetBundles().size(); return 0; } IPerspectiveRegistry* WorkbenchPlugin::GetPerspectiveRegistry() { if (perspRegistry == nullptr) { perspRegistry = new PerspectiveRegistry(); // the load methods can touch on WorkbenchImages if an image is // missing so we need to wrap the call in // a startup block for the case where a custom descriptor exists on // startup that does not have an image // associated with it. See bug 196352. //StartupThreading.runWithoutExceptions(new StartupRunnable() { // public void runWithException() throws Throwable { perspRegistry->Load(); // } //}); } return perspRegistry; } // PreferenceManager getPreferenceManager() { // if (preferenceManager == null) { // preferenceManager = new WorkbenchPreferenceManager( // PREFERENCE_PAGE_CATEGORY_SEPARATOR); // // //Get the pages from the registry // PreferencePageRegistryReader registryReader = new PreferencePageRegistryReader( // getWorkbench()); // registryReader // .loadFromRegistry(Platform.getExtensionRegistry()); // preferenceManager.addPages(registryReader.getTopLevelNodes()); // // } // return preferenceManager; // } IIntroRegistry* WorkbenchPlugin::GetIntroRegistry() { if (introRegistry == nullptr) { introRegistry = new IntroRegistry(); } return introRegistry; } IViewRegistry* WorkbenchPlugin::GetViewRegistry() { if (!viewRegistry) viewRegistry = new ViewRegistry(); return viewRegistry; } IEditorRegistry* WorkbenchPlugin::GetEditorRegistry() { if (!editorRegistry) editorRegistry = new EditorRegistry(); return editorRegistry; } IElementFactory* WorkbenchPlugin::GetElementFactory(const QString& targetID) const { // Get the extension point registry. IExtensionPoint::Pointer extensionPoint = Platform::GetExtensionRegistry()->GetExtensionPoint( PlatformUI::PLUGIN_ID(), WorkbenchRegistryConstants::PL_ELEMENT_FACTORY); IElementFactory* factory = nullptr; if (!extensionPoint) { WorkbenchPlugin::Log("Unable to find element factory. Extension point: " + WorkbenchRegistryConstants::PL_ELEMENT_FACTORY + " not found"); return factory; } // Loop through the config elements. IConfigurationElement::Pointer targetElement; QList configElements = extensionPoint->GetConfigurationElements(); for (int j = 0; j < configElements.size(); j++) { QString strID = configElements[j]->GetAttribute("id"); if (targetID == strID) { targetElement = configElements[j]; break; } } if (!targetElement) { // log it since we cannot safely display a dialog. WorkbenchPlugin::Log("Unable to find element factory: " + targetID); return factory; } // Create the extension. try { factory = targetElement->CreateExecutableExtension("class"); } catch (const CoreException& e) { // log it since we cannot safely display a dialog. WorkbenchPlugin::Log("Unable to create element factory.", e.GetStatus()); factory = nullptr; } return factory; } IPresentationFactory* WorkbenchPlugin::GetPresentationFactory() { if (presentationFactory != nullptr) return presentationFactory; QString targetID = Workbench::GetInstance()->GetPresentationId(); presentationFactory = this->CreateExtension( WorkbenchRegistryConstants::PL_PRESENTATION_FACTORIES, "factory", targetID); if (presentationFactory == nullptr) WorkbenchPlugin::Log("Error creating presentation factory: " + targetID + " -- class is not an IPresentationFactory"); return presentationFactory; } void WorkbenchPlugin::Log(const QString& message) { BERRY_INFO << "LOG: " << message << std::endl; //inst->GetLog().log(message); } void WorkbenchPlugin::Log(const ctkException &exc) { QString str; QDebug dbg(&str); dbg << exc.printStackTrace(); BERRY_INFO << "LOG: " << str << std::endl; //inst->GetLog().log(exc); } void WorkbenchPlugin::Log(const QString& message, const ctkException &t) { PlatformException exc(message, t); WorkbenchPlugin::Log(exc); } void WorkbenchPlugin::Log(const QString& clazz, const QString& methodName, const ctkException &t) { QString msg = QString("Exception in ") + clazz + "." + methodName + ": " + t.what(); WorkbenchPlugin::Log(msg, t); } void WorkbenchPlugin::Log(const QString& message, const SmartPointer& status) { //1FTUHE0: ITPCORE:ALL - API - Status & logging - loss of semantic info if (!message.isEmpty()) { GetDefault()->GetLog()->Log(StatusUtil::NewStatus(IStatus::ERROR_TYPE, message, BERRY_STATUS_LOC)); } GetDefault()->GetLog()->Log(status); } void WorkbenchPlugin::Log(const SmartPointer& status) { GetDefault()->GetLog()->Log(status); } void WorkbenchPlugin::start(ctkPluginContext* context) { + // Dummy code to force linkage to Qt5PrintSupport (issue with GCC 7.3) + QPrinterInfo forceQt5PrintSupportLinkage; + forceQt5PrintSupportLinkage.isNull(); + + // Same for Qt5Svg + QSvgGenerator forceQt5SvgLinkage; + forceQt5SvgLinkage.title(); + //context.addBundleListener(getBundleListener()); AbstractUICTKPlugin::start(context); bundleContext = context; AbstractSourceProvider::DEBUG = Policy::DEBUG_SOURCES(); HandlerAuthority::DEBUG = Policy::DEBUG_HANDLERS(); HandlerAuthority::DEBUG_PERFORMANCE = Policy::DEBUG_HANDLERS_PERFORMANCE(); HandlerAuthority::DEBUG_VERBOSE = Policy::DEBUG_HANDLERS_VERBOSE(); HandlerAuthority::DEBUG_VERBOSE_COMMAND_ID = Policy::DEBUG_HANDLERS_VERBOSE_COMMAND_ID(); BERRY_REGISTER_EXTENSION_CLASS(EditorIntroAdapterPart, context) BERRY_REGISTER_EXTENSION_CLASS(ExtensionFactory, context) BERRY_REGISTER_EXTENSION_CLASS(QtWidgetsTweaklet, context) BERRY_REGISTER_EXTENSION_CLASS(QtWorkbenchTweaklet, context) BERRY_REGISTER_EXTENSION_CLASS(QtWorkbenchPageTweaklet, context) BERRY_REGISTER_EXTENSION_CLASS(QtWorkbenchPresentationFactory, context) BERRY_REGISTER_EXTENSION_CLASS(PerspectivesPreferencePage, context) BERRY_REGISTER_EXTENSION_CLASS(QtStylePreferencePage, context) BERRY_REGISTER_EXTENSION_CLASS(HandlerServiceFactory, context) BERRY_REGISTER_EXTENSION_CLASS(MenuServiceFactory, context) BERRY_REGISTER_EXTENSION_CLASS(CommandServiceFactory, context) BERRY_REGISTER_EXTENSION_CLASS(WorkbenchSourceProvider, context) BERRY_REGISTER_EXTENSION_CLASS(OpenPerspectivePropertyTester, context) BERRY_REGISTER_EXTENSION_CLASS(PerspectiveParameterValues, context) BERRY_REGISTER_EXTENSION_CLASS(HelpContentsHandler, context) BERRY_REGISTER_EXTENSION_CLASS(DynamicHelpHandler, context) BERRY_REGISTER_EXTENSION_CLASS(IntroHandler, context) BERRY_REGISTER_EXTENSION_CLASS(OpenInNewWindowHandler, context) BERRY_REGISTER_EXTENSION_CLASS(NewEditorHandler, context) BERRY_REGISTER_EXTENSION_CLASS(QuitHandler, context) BERRY_REGISTER_EXTENSION_CLASS(ShowPerspectiveHandler, context) BERRY_REGISTER_EXTENSION_CLASS(ShowViewHandler, context) BERRY_REGISTER_EXTENSION_CLASS(SavePerspectiveHandler, context) BERRY_REGISTER_EXTENSION_CLASS(ClosePerspectiveHandler, context) BERRY_REGISTER_EXTENSION_CLASS(CloseAllPerspectivesHandler, context) BERRY_REGISTER_EXTENSION_CLASS(ResetPerspectiveHandler, context) styleManager.reset(new QtStyleManager()); context->registerService(styleManager.data()); // The UI plugin needs to be initialized so that it can install the callback in PrefUtil, // which needs to be done as early as possible, before the workbench // accesses any API preferences. // Bundle uiBundle = Platform.getBundle(PlatformUI.PLUGIN_ID); // try // { // // Attempt to load the activator of the ui bundle. This will force lazy start // // of the ui bundle. Using the bundle activator class here because it is a // // class that needs to be loaded anyway so it should not cause extra classes // // to be loaded. // if(uiBundle != null) // uiBundle.loadClass(UI_BUNDLE_ACTIVATOR); // } // catch (ClassNotFoundException e) // { // WorkbenchPlugin.log("Unable to load UI activator", e); //$NON-NLS-1$ // } /* * DO NOT RUN ANY OTHER CODE AFTER THIS LINE. If you do, then you are * likely to cause a deadlock in class loader code. Please see Bug 86450 * for more information. */ } //const QList WorkbenchPlugin::GetBundles() //{ // return bundleContext.IsNull() ? QList() : bundleContext->GetBundles(); //} ctkPluginContext* WorkbenchPlugin::GetPluginContext() { return bundleContext; } void WorkbenchPlugin::stop(ctkPluginContext* context) { AbstractUICTKPlugin::stop(context); styleManager.reset(); delete perspRegistry; // avoid possible crash, see bug #18399 perspRegistry = nullptr; } QString WorkbenchPlugin::GetDataLocation() const { QFileInfo fileInfo = bundleContext->getDataFile(""); if (!fileInfo.isWritable()) return QString(); return fileInfo.absoluteFilePath(); } } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeShowSelectedNodesAction.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeShowSelectedNodesAction.cpp index a2a4af5796..a74a05898a 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeShowSelectedNodesAction.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkDataNodeShowSelectedNodesAction.cpp @@ -1,70 +1,79 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include // mitk core #include QmitkDataNodeShowSelectedNodesAction::QmitkDataNodeShowSelectedNodesAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(workbenchpartSite) { setText(tr("Show only selected nodes")); InitializeAction(); } QmitkDataNodeShowSelectedNodesAction::QmitkDataNodeShowSelectedNodesAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchpartSite) : QAction(parent) , QmitkAbstractDataNodeAction(berry::IWorkbenchPartSite::Pointer(workbenchpartSite)) { setText(tr("Show only selected nodes")); InitializeAction(); } void QmitkDataNodeShowSelectedNodesAction::InitializeAction() { connect(this, &QmitkDataNodeShowSelectedNodesAction::triggered, this, &QmitkDataNodeShowSelectedNodesAction::OnActionTriggered); } void QmitkDataNodeShowSelectedNodesAction::OnActionTriggered(bool /*checked*/) { if (m_DataStorage.IsExpired()) { return; } mitk::BaseRenderer::Pointer baseRenderer = GetBaseRenderer(); auto dataNodes = GetSelectedNodes(); auto nodeset = m_DataStorage.Lock()->GetAll(); for (auto& node : *nodeset) { if (node.IsNotNull()) { - node->SetVisibility(dataNodes.contains(node), baseRenderer); + node->SetVisibility(dataNodes.contains(node)); + if(node->GetData() == nullptr) + { + node->SetVisibility(true); + mitk::DataStorage::SetOfObjects::ConstPointer derivations = m_DataStorage.Lock()->GetDerivations(node); + for (mitk::DataStorage::SetOfObjects::const_iterator iter = derivations->begin(); iter != derivations->end(); ++iter) + { + (*iter)->SetVisibility(true); + } + } } } if (nullptr == baseRenderer) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else { mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); } } diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/CMakeLists.txt b/Plugins/org.mitk.gui.qt.bonesegmentation/CMakeLists.txt new file mode 100644 index 0000000000..537a83c7a2 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/CMakeLists.txt @@ -0,0 +1,7 @@ +project(org_mitk_gui_qt_bonesegmentation) + +mitk_create_plugin( + EXPORT_DIRECTIVE BONESEGMENTATION_EXPORT + EXPORTED_INCLUDE_SUFFIXES src + MODULE_DEPENDS MitkQtWidgetsExt +) diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.bonesegmentation/documentation/UserManual/Manual.dox new file mode 100644 index 0000000000..d556a8b7b1 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/documentation/UserManual/Manual.dox @@ -0,0 +1,17 @@ +/** +\page org_mitk_gui_qt_bonesegmentation The Bonesegmentation + +\imageMacro{icon.png,"Icon of Bonesegmentation",2.00} + +\tableofcontents + +\section org_mitk_gui_qt_bonesegmentationOverview Overview +Describe the features of your awesome plugin here +
    +
  • Increases productivity +
  • Creates beautiful images +
  • Generates PhD thesis +
  • Brings world peace +
+ +*/ diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/documentation/UserManual/icon.xpm b/Plugins/org.mitk.gui.qt.bonesegmentation/documentation/UserManual/icon.xpm new file mode 100644 index 0000000000..9057c20bc6 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/documentation/UserManual/icon.xpm @@ -0,0 +1,21 @@ +/* XPM */ +static const char * icon_xpm[] = { +"16 16 2 1", +" c #FF0000", +". c #000000", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/documentation/doxygen/modules.dox b/Plugins/org.mitk.gui.qt.bonesegmentation/documentation/doxygen/modules.dox new file mode 100644 index 0000000000..6d9975611f --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/documentation/doxygen/modules.dox @@ -0,0 +1,16 @@ +/** + \defgroup org_mitk_gui_qt_bonesegmentation org.mitk.gui.qt.bonesegmentation + \ingroup MITKPlugins + + \brief Describe your plugin here. + +*/ + +/** + \defgroup org_mitk_gui_qt_bonesegmentation_internal Internal + \ingroup org_mitk_gui_qt_bonesegmentation + + \brief This subcategory includes the internal classes of the org.mitk.gui.qt.bonesegmentation plugin. Other + plugins must not rely on these classes. They contain implementation details and their interface + may change at any time. We mean it. +*/ diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/files.cmake b/Plugins/org.mitk.gui.qt.bonesegmentation/files.cmake new file mode 100644 index 0000000000..144caa29eb --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/files.cmake @@ -0,0 +1,42 @@ +set(SRC_CPP_FILES + +) + +set(INTERNAL_CPP_FILES + org_mitk_gui_qt_bonesegmentation_Activator.cpp + BoneSegmentation.cpp +) + +set(UI_FILES + src/internal/BoneSegmentationControls.ui +) + +set(MOC_H_FILES + src/internal/org_mitk_gui_qt_bonesegmentation_Activator.h + src/internal/BoneSegmentation.h +) + +# list of resource files which can be used by the plug-in +# system without loading the plug-ins shared library, +# for example the icon used in the menu and tabs for the +# plug-in views in the workbench +set(CACHED_RESOURCE_FILES + resources/icon.svg + plugin.xml +) + +# list of Qt .qrc files which contain additional resources +# specific to this plugin +set(QRC_FILES + resources/boneseg.qrc +) + +set(CPP_FILES ) + +foreach(file ${SRC_CPP_FILES}) + set(CPP_FILES ${CPP_FILES} src/${file}) +endforeach(file ${SRC_CPP_FILES}) + +foreach(file ${INTERNAL_CPP_FILES}) + set(CPP_FILES ${CPP_FILES} src/internal/${file}) +endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.bonesegmentation/manifest_headers.cmake new file mode 100644 index 0000000000..d85840cc19 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/manifest_headers.cmake @@ -0,0 +1,5 @@ +set(Plugin-Name "Bonesegmentation") +set(Plugin-Version "0.1") +set(Plugin-Vendor "DKFZ") +set(Plugin-ContactAddress "") +set(Require-Plugin org.mitk.gui.qt.common) diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/plugin.xml b/Plugins/org.mitk.gui.qt.bonesegmentation/plugin.xml new file mode 100644 index 0000000000..b0ce58d556 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/plugin.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/resources/boneseg.qrc b/Plugins/org.mitk.gui.qt.bonesegmentation/resources/boneseg.qrc new file mode 100644 index 0000000000..b1940d6050 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/resources/boneseg.qrc @@ -0,0 +1,5 @@ + + + segment.py + + diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/resources/icon.svg b/Plugins/org.mitk.gui.qt.bonesegmentation/resources/icon.svg new file mode 100644 index 0000000000..4d4941c235 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/resources/icon.svg @@ -0,0 +1,2413 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/resources/segment.py b/Plugins/org.mitk.gui.qt.bonesegmentation/resources/segment.py new file mode 100644 index 0000000000..ab37dd0b65 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/resources/segment.py @@ -0,0 +1,385 @@ +import os +import numpy as np +import SimpleITK +import torch +import torch.cuda + +print('Started python script...') + +import SimpleITK as sitk +from skimage.transform import resize +import sys + + +def resize_image(image, old_spacing, new_spacing, order=3): + new_shape = (int(np.round(old_spacing[0]/new_spacing[0]*float(image.shape[0]))), + int(np.round(old_spacing[1]/new_spacing[1]*float(image.shape[1]))), + int(np.round(old_spacing[2]/new_spacing[2]*float(image.shape[2])))) + return resize(image, new_shape, order=order, mode='edge') + + +def cut_off_values_upper_lower_percentile(image, mask=None, percentile_lower=0.2, percentile_upper=99.8): + if mask is None: + mask = image!=image[0,0,0] + cut_off_lower = np.percentile(image[mask!=0].ravel(), percentile_lower) + cut_off_upper = np.percentile(image[mask!=0].ravel(), percentile_upper) + res = np.copy(image) + res[(res < cut_off_lower) & (mask !=0 )] = cut_off_lower + res[(res > cut_off_upper) & (mask !=0 )] = cut_off_upper + return image + + +def preprocess_image(itk_image, is_seg=False, spacing_target=(1, 0.5, 0.5)): + spacing = np.array(itk_image.GetSpacing())[[2, 1, 0]] + image = sitk.GetArrayFromImage(itk_image).astype(float) + if not is_seg: + image = resize_image(image, spacing, spacing_target).astype(np.float32) + # cut off outliers + image = cut_off_values_upper_lower_percentile(image, np.ones(image.shape), 1., 99.) + #subtract mean, divide by std. use heuristic masking + image -= image.mean() + image /= image.std() + else: + image = resize_image(image, spacing, spacing_target, 0) + return image + + +def load_and_preprocess(in_image): + images = {} + + np_array = sitk.GetArrayFromImage(in_image).astype(float) + if len(np_array.shape) > 3: + b0 = sitk.GetImageFromArray(np_array[:,:,:,0]) + b0.SetSpacing(in_image.GetSpacing()) + b0.SetOrigin(in_image.GetOrigin()) + b0.SetDirection(in_image.GetDirection()) + images["T1"] = b0 + elif len(np_array.shape)==3 : + images["T1"] = in_image + + properties_dict = { + "spacing": in_image.GetSpacing(), + "direction": in_image.GetDirection(), + "size": in_image.GetSize(), + "origin": in_image.GetOrigin() + } + + for k in images.keys(): + images[k] = preprocess_image(images[k], is_seg=False, spacing_target=(1.5, 1.5, 1.5)) + + properties_dict['size_before_cropping'] = images["T1"].shape + + imgs = [] + for seq in ['T1']: + imgs.append(images[seq][None]) + all_data = np.vstack(imgs) + return all_data, properties_dict + +def get_sitk_from_nparray(segmentation, original_image, dct) : + ''' + segmentation must have the same spacing as the original nifti (for now). segmentation may have been cropped out + of the original image + :param segmentation: + :param dct: + :return: + ''' + old_size = np.array(dct['size_before_cropping']) + bbox = dct.get('brain_bbox') + if bbox is not None: + seg_old_size = np.zeros(old_size) + for c in range(3): + bbox[c][1] = np.min((bbox[c][0] + segmentation.shape[c], old_size[c])) + seg_old_size[bbox[0][0]:bbox[0][1], + bbox[1][0]:bbox[1][1], + bbox[2][0]:bbox[2][1]] = segmentation + else: + seg_old_size = segmentation + + seg_old_spacing = resize_segmentation(seg_old_size, np.array(dct['size'])[[2, 1, 0]], order=3) + seg_resized_itk = sitk.GetImageFromArray(seg_old_spacing.astype(np.uint8)) + seg_resized_itk.SetSpacing(np.array(dct['spacing'])[[0, 1, 2]]) + seg_resized_itk.SetOrigin(dct['origin']) + seg_resized_itk.SetDirection(dct['direction']) + + if original_image is not None : + image = sitk.GetArrayFromImage(original_image).astype(float) + + if len(image.shape) > 3 : + for i in range(image.shape[3]) : + image[:,:,:,i] *= seg_old_spacing + else : + image *= seg_old_spacing + + brain_extracted = sitk.GetImageFromArray(image.astype(np.float32)) + brain_extracted.SetSpacing(np.array(dct['spacing'])[[0, 1, 2]]) + brain_extracted.SetOrigin(dct['origin']) + brain_extracted.SetDirection(dct['direction']) + else : + brain_extracted = None + + return seg_resized_itk, brain_extracted + +def save_segmentation_nifti(segmentation, dct, out_fname): + ''' + segmentation must have the same spacing as the original nifti (for now). segmentation may have been cropped out + of the original image + :param segmentation: + :param dct: + :param out_fname: + :return: + ''' + old_size = np.array(dct['size_before_cropping']) + bbox = dct.get('brain_bbox') + if bbox is not None: + seg_old_size = np.zeros(old_size) + for c in range(3): + bbox[c][1] = np.min((bbox[c][0] + segmentation.shape[c], old_size[c])) + seg_old_size[bbox[0][0]:bbox[0][1], + bbox[1][0]:bbox[1][1], + bbox[2][0]:bbox[2][1]] = segmentation + else: + seg_old_size = segmentation + + seg_old_spacing = resize_segmentation(seg_old_size, np.array(dct['size'])[[2, 1, 0]], order=3) + seg_resized_itk = sitk.GetImageFromArray(seg_old_spacing.astype(np.uint8)) + seg_resized_itk.SetSpacing(np.array(dct['spacing'])[[0, 1, 2]]) + seg_resized_itk.SetOrigin(dct['origin']) + seg_resized_itk.SetDirection(dct['direction']) + sitk.WriteImage(seg_resized_itk, out_fname) + + +def resize_segmentation(segmentation, new_shape, order=3): + unique_labels = np.unique(segmentation) + assert len(segmentation.shape) == len(new_shape), "new shape must have same dimensionality as segmentation" + reshaped_multihot = np.zeros([len(unique_labels)] + list(new_shape), dtype=float) + for i, c in enumerate(unique_labels): + reshaped_multihot[i] = resize((segmentation == c).astype(float), new_shape, order, mode="constant", cval=0, clip=True) + reshaped = unique_labels[np.argmax(reshaped_multihot, 0)] + return reshaped.astype(segmentation.dtype) + + +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2017 Division of Medical Image Computing, German Cancer Research Center (DKFZ) +# +# 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 +# +# 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. + +import numpy as np + + +def reshape(orig_img, append_value=-1024, new_shape=(512, 512, 512)): + reshaped_image = np.zeros(new_shape) + reshaped_image[...] = append_value + x_offset = 0 + y_offset = 0 # (new_shape[1] - orig_img.shape[1]) // 2 + z_offset = 0 # (new_shape[2] - orig_img.shape[2]) // 2 + + reshaped_image[x_offset:orig_img.shape[0]+x_offset, y_offset:orig_img.shape[1]+y_offset, z_offset:orig_img.shape[2]+z_offset] = orig_img + # insert temp_img.min() as background value + + return reshaped_image + + +def crop_image_to_orig_size(image, orig_shape): + x_offset = 0 + y_offset = 0 # (512 - orig_shape[1]) // 2 + z_offset = 0 # (512 - orig_shape[2]) // 2 + + return image[x_offset:orig_shape[0] + x_offset, y_offset:orig_shape[1] + y_offset, z_offset:orig_shape[2] + z_offset] + +def next_power_of_2(x): + return 1 if x == 0 else 2**(x - 1).bit_length() + +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2017 Division of Medical Image Computing, German Cancer Research Center (DKFZ) +# +# 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 +# +# 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. + +# Defines the Unet. +# |num_downs|: number of downsamplings in UNet. For example, +# if |num_downs| == 7, image of size 128x128 will become of size 1x1 at the bottleneck + +# recursive implementation of Unet +import torch + +from torch import nn + + +class UNet(nn.Module): + def __init__(self, num_classes=3, in_channels=1, initial_filter_size=64, kernel_size=3, num_downs=4, norm_layer=nn.InstanceNorm2d): + # norm_layer=nn.BatchNorm2d, use_dropout=False): + super(UNet, self).__init__() + + # construct unet structure + unet_block = UnetSkipConnectionBlock(in_channels=initial_filter_size * 2 ** (num_downs-1), out_channels=initial_filter_size * 2 ** num_downs, + num_classes=num_classes, kernel_size=kernel_size, norm_layer=norm_layer, innermost=True) + for i in range(1, num_downs): + unet_block = UnetSkipConnectionBlock(in_channels=initial_filter_size * 2 ** (num_downs-(i+1)), + out_channels=initial_filter_size * 2 ** (num_downs-i), + num_classes=num_classes, kernel_size=kernel_size, submodule=unet_block, norm_layer=norm_layer) + unet_block = UnetSkipConnectionBlock(in_channels=in_channels, out_channels=initial_filter_size, + num_classes=num_classes, kernel_size=kernel_size, submodule=unet_block, norm_layer=norm_layer, + outermost=True) + + self.model = unet_block + + def forward(self, x): + return self.model(x) + + +# Defines the submodule with skip connection. +# X -------------------identity---------------------- X +# |-- downsampling -- |submodule| -- upsampling --| +class UnetSkipConnectionBlock(nn.Module): + def __init__(self, in_channels=None, out_channels=None, num_classes=1, kernel_size=3, + submodule=None, outermost=False, innermost=False, norm_layer=nn.InstanceNorm2d, use_dropout=False): + super(UnetSkipConnectionBlock, self).__init__() + self.outermost = outermost + # downconv + pool = nn.MaxPool2d(2, stride=2) + conv1 = self.contract(in_channels=in_channels, out_channels=out_channels, kernel_size=kernel_size, norm_layer=norm_layer) + conv2 = self.contract(in_channels=out_channels, out_channels=out_channels, kernel_size=kernel_size, norm_layer=norm_layer) + + # upconv + conv3 = self.expand(in_channels=out_channels*2, out_channels=out_channels, kernel_size=kernel_size) + conv4 = self.expand(in_channels=out_channels, out_channels=out_channels, kernel_size=kernel_size) + + if outermost: + final = nn.Conv2d(out_channels, num_classes, kernel_size=1) + down = [conv1, conv2] + up = [conv3, conv4, final] + model = down + [submodule] + up + elif innermost: + upconv = nn.ConvTranspose2d(in_channels*2, in_channels, + kernel_size=2, stride=2) + model = [pool, conv1, conv2, upconv] + else: + upconv = nn.ConvTranspose2d(in_channels*2, in_channels, kernel_size=2, stride=2) + + down = [pool, conv1, conv2] + up = [conv3, conv4, upconv] + + if use_dropout: + model = down + [submodule] + up + [nn.Dropout(0.5)] + else: + model = down + [submodule] + up + + self.model = nn.Sequential(*model) + + @staticmethod + def contract(in_channels, out_channels, kernel_size=3, norm_layer=nn.InstanceNorm2d): + layer = nn.Sequential( + nn.Conv2d(in_channels, out_channels, kernel_size, padding=1), + norm_layer(out_channels), + nn.LeakyReLU(inplace=True)) + return layer + + @staticmethod + def expand(in_channels, out_channels, kernel_size=3): + layer = nn.Sequential( + nn.Conv2d(in_channels, out_channels, kernel_size, padding=1), + nn.LeakyReLU(inplace=True), + ) + return layer + + @staticmethod + def center_crop(layer, target_width, target_height): + batch_size, n_channels, layer_width, layer_height = layer.size() + xy1 = (layer_width - target_width) // 2 + xy2 = (layer_height - target_height) // 2 + return layer[:, :, xy1:(xy1 + target_width), xy2:(xy2 + target_height)] + + def forward(self, x): + if self.outermost: + return self.model(x) + else: + crop = self.center_crop(self.model(x), x.size()[2], x.size()[3]) + return torch.cat([x, crop], 1) + +batch_size = 8 +num_classes = 2 +dir_path = os.path.dirname(os.path.realpath(__file__)) +seg_load_network_path = os.path.join(dir_path, 'trained_bone_seg_unet.pth.tar') +device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') + +# print(sys.argv) + +model = UNet(num_classes=num_classes, in_channels=1) +model.load_state_dict(torch.load( + seg_load_network_path, map_location=device)) +print(device) + +nrrd_image = SimpleITK.ReadImage(os.path.join(dir_path, 'temp_CT.nrrd')) +# nrrd_image = SimpleITK.ReadImage(sys.argv[1]) + +model.eval() +model.to(device) + +result = [] +first = True +print('Segmenting...') + +# nrrd_image = in_image +input_image_array = SimpleITK.GetArrayFromImage(nrrd_image) +append_empty_slices = batch_size - (input_image_array.shape[0]%batch_size) + +y = next_power_of_2(input_image_array.shape[1]) +z = next_power_of_2(input_image_array.shape[2]) + +if y > 512 or z > 512: + batch_size = 2 +temp_array = np.ones((input_image_array.shape[0] + append_empty_slices, y, z))*(-1024) +temp_array[0:input_image_array.shape[0], 0:input_image_array.shape[1], 0:input_image_array.shape[2]] = input_image_array +with torch.no_grad(): + start = 0 + end = start+batch_size + while end <= temp_array.shape[0]: + pred = model(torch.from_numpy(np.expand_dims( + temp_array[start:end], axis=1).astype(np.float32)).to(device)) + if first: + result = pred.detach().data.cpu() + first = False + else: + result = torch.cat((result, pred.detach().data.cpu())) + print(result.shape) + start = end + end = start + batch_size +result = torch.argmax(result, dim=1, keepdim=True) + +array_to_write = result.data.numpy().squeeze() +array_to_write = array_to_write[0:input_image_array.shape[0], 0:input_image_array.shape[1], 0:input_image_array.shape[2]] +array_to_write = crop_image_to_orig_size( array_to_write, SimpleITK.GetArrayFromImage(nrrd_image).shape) +print(array_to_write.shape) + +image_to_write = SimpleITK.GetImageFromArray(array_to_write) + +image_to_write.SetSpacing(nrrd_image.GetSpacing()) +image_to_write.SetOrigin(nrrd_image.GetOrigin()) +image_to_write.SetDirection(nrrd_image.GetDirection()) + +SimpleITK.WriteImage(SimpleITK.Cast(image_to_write, SimpleITK.sitkUInt8), os.path.join(dir_path, 'temp_bone_seg.nrrd')) + +print('Ended python script...') diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/BoneSegmentation.cpp b/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/BoneSegmentation.cpp new file mode 100644 index 0000000000..b64461fe8a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/BoneSegmentation.cpp @@ -0,0 +1,233 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +// Blueberry +#include +#include + +// Qmitk +#include "BoneSegmentation.h" + +// Qt +#include +#include +#include + +// mitk image +#include +#include + +const std::string BoneSegmentation::VIEW_ID = "org.mitk.views.bonesegmentation"; + +void BoneSegmentation::SetFocus() +{ + m_Controls.buttonPerformImageProcessing->setFocus(); +} + +void BoneSegmentation::CreateQtPartControl(QWidget *parent) +{ + m_Controls.setupUi(parent); + m_ProcessBoneSegmentationPython = new QProcess(); + m_Controls.buttonPerformImageProcessing->setText("Start Bone Segmentation"); + m_Controls.buttonPerformImageProcessing->setEnabled(false); + + m_Controls.m_ImageSelector->SetDataStorage(GetDataStorage()); + m_Controls.m_ImageSelector->SetPredicate(GetImagePredicate()); + + // Connects + connect(m_Controls.buttonPerformImageProcessing, &QPushButton::clicked, this, &BoneSegmentation::DoImageProcessing); + connect(m_Controls.m_ButtonLoadTrainedNet, &QPushButton::clicked, this, &BoneSegmentation::DoLoadTrainedNet); + connect(m_ProcessBoneSegmentationPython, SIGNAL(finished(int, QProcess::ExitStatus )),this, SLOT(DoBoneSegmentationProcessFinished())); + connect(&m_ThreadSaveTempNrrdImage, SIGNAL(started()), this, SLOT(DoSaveTempNrrd())); + connect(&m_ThreadSaveTempNrrdImage, SIGNAL(finished()), this, SLOT(DoStartBoneSegmentationProcess())); + connect(this->m_Controls.m_ImageSelector, + static_cast(&QComboBox::currentIndexChanged), + this, + &BoneSegmentation::OnImageSelectorChanged); + + if(QFile::exists(QString::fromStdString(mitk::IOUtil::GetTempPath() + "trained_bone_seg_unet.pth.tar"))) + { + m_Controls.labelLoadTrainedNet->setText("Network loaded!"); + } + else + { + m_Controls.labelLoadTrainedNet->setText("Please load a trained network!"); + } +} + +void BoneSegmentation::OnImageSelectorChanged() +{ + auto selectedImageNode = m_Controls.m_ImageSelector->GetSelectedNode(); + if (selectedImageNode != m_selectedImageNode) + { + m_selectedImageNode = selectedImageNode; + if (m_selectedImageNode.IsNotNull()) + { + m_Controls.labelWarning->setVisible(false); + m_Controls.buttonPerformImageProcessing->setEnabled(true); + return; + } + m_Controls.labelWarning->setText("Please select an image!"); + m_Controls.labelWarning->setVisible(true); + m_Controls.buttonPerformImageProcessing->setEnabled(false); + } +} + +void BoneSegmentation::DoLoadTrainedNet() +{ + QString pretrainedNetResourcesPath = QFileDialog::getOpenFileName(nullptr, tr("Open File"), + mitk::IOUtil::GetTempPath(), + tr("Images (*.pth.tar)")); + + if(QFile::exists(QString::fromStdString(mitk::IOUtil::GetTempPath() + "trained_bone_seg_unet.pth.tar"))) + { + QFile::remove(QString::fromStdString(mitk::IOUtil::GetTempPath() + "trained_bone_seg_unet.pth.tar")); + } + QString pretrainedNetTmpPath = QString::fromStdString(mitk::IOUtil::GetTempPath() + "trained_bone_seg_unet.pth.tar"); + QFile::copy(pretrainedNetResourcesPath, pretrainedNetTmpPath); + m_Controls.labelLoadTrainedNet->setText("Network loaded!"); +} + +void BoneSegmentation::DoSaveTempNrrd() +{ + if (!m_selectedImageNode) + { + // Nothing selected. Inform the user and return + QMessageBox::information(nullptr, "Template", "Please load and select an image before starting image processing."); + return; + } + + mitk::BaseData *data = m_selectedImageNode->GetData(); + if (data) + { + // test if this data item is an image or not (could also be a surface or something totally different) + mitk::Image *mitk_image = dynamic_cast(data); + if (mitk_image) + { + std::stringstream message; + std::string name; + message << "Performing image processing for image "; + if (m_selectedImageNode->GetName(name)) + { + // a property called "name" was found for this DataNode + message << "'" << name << "'"; + } + message << "."; + MITK_INFO << message.str(); + + MITK_INFO << "[START] StartPythonProcess()"; + + std::string file_name = mitk::IOUtil::GetTempPath() + m_TempNrrdFile; + if(QFile::exists(QString::fromStdString(file_name))) + { + QFile::remove(QString::fromStdString(file_name)); + } + std::string file_name_temp_seg = mitk::IOUtil::GetTempPath() + m_TempBoneSegFile + ".nrrd"; + if(QFile::exists(QString::fromStdString(file_name_temp_seg))) + { + QFile::remove(QString::fromStdString(file_name_temp_seg)); + } + mitk::IOUtil::Save(mitk_image, file_name); + MITK_INFO << "Temporary NRRD image saved!"; + } + } + + m_ThreadSaveTempNrrdImage.quit(); +} + +void BoneSegmentation::DoImageProcessing() +{ + m_Controls.labelWarning->setVisible(true); + m_Controls.labelWarning->setText("This might take a while.\nDepending ob your machine up to 20 minutes or more."); + m_Controls.buttonPerformImageProcessing->setEnabled(false); + m_Controls.m_ButtonLoadTrainedNet->setEnabled(false); + m_Controls.m_ImageSelector->setEnabled(false); + m_ThreadSaveTempNrrdImage.start(); +} + +void BoneSegmentation::DoStartBoneSegmentationProcess() +{ + MITK_INFO << "[Start] DoStartBoneSegmentationProcess()"; + + // Copy python file and pretrained net to /tmp/ folder + QString fileName(":/" + m_PythonFileName); + QString pythonFileName = QString::fromStdString(mitk::IOUtil::GetTempPath()) + m_PythonFileName; + + QFile::copy(fileName, pythonFileName); + + QString programCall("python"); + QStringList arguments = QStringList() << pythonFileName; + + MITK_INFO << programCall; + m_ProcessBoneSegmentationPython->start(programCall, arguments); + + MITK_INFO << "[END] DoStartBoneSegmentationProcess()"; +} + +void BoneSegmentation::DoBoneSegmentationProcessFinished() +{ + MITK_INFO << "Ended calculation thread."; + + try + { + mitk::IOUtil::Load(mitk::IOUtil::GetTempPath() + m_TempBoneSegFile + ".nrrd", *GetDataStorage()); + GetDataStorage().GetPointer()->GetNamedNode(m_TempBoneSegFile)->SetName("Bone_seg_" + m_selectedImageNode->GetName()); + } + catch(...) + { + MITK_WARN << "Error loading output file. Maybe it does not exist." ; + } + + std::string temp_CT = mitk::IOUtil::GetTempPath() + m_TempNrrdFile + ".nrrd"; + if(remove(temp_CT.c_str()) != 0) + { + MITK_WARN << "Error deleting file: " + m_TempNrrdFile; + } + else + { + MITK_INFO << "File successfully deleted: " + m_TempNrrdFile; + } + + std::string temp_output = mitk::IOUtil::GetTempPath() + m_TempBoneSegFile; + if(remove(temp_output.c_str()) != 0) + { + MITK_WARN << "Error deleting file: " + m_TempBoneSegFile; + } + else + { + MITK_INFO << "File successfully deleted: " + m_TempBoneSegFile; + } + + m_Controls.buttonPerformImageProcessing->setText("Start Bone Segmentation"); + m_Controls.buttonPerformImageProcessing->setEnabled(true); + m_Controls.m_ButtonLoadTrainedNet->setEnabled(true); + m_Controls.labelWarning->setVisible(false); + m_Controls.m_ImageSelector->setEnabled(true); +} + +mitk::NodePredicateBase::Pointer BoneSegmentation::GetImagePredicate() +{ + auto isImage = mitk::NodePredicateDataType::New("Image"); + auto hasBinaryProperty = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); + auto isNotBinary = mitk::NodePredicateNot::New(hasBinaryProperty); + auto isNotBinaryImage = mitk::NodePredicateAnd::New(isImage, isNotBinary); + auto hasHelperObjectProperty = mitk::NodePredicateProperty::New("helper object", nullptr); + auto isNoHelperObject = mitk::NodePredicateNot::New(hasHelperObjectProperty); + auto isNoHelperObjectPredicate = isNoHelperObject.GetPointer(); + + auto isImageForImageStatistics = mitk::NodePredicateAnd::New(isNotBinaryImage, isNoHelperObjectPredicate); + return isImageForImageStatistics.GetPointer(); +} diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/BoneSegmentation.h b/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/BoneSegmentation.h new file mode 100644 index 0000000000..78742850bb --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/BoneSegmentation.h @@ -0,0 +1,85 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + + +#ifndef BoneSegmentation_h +#define BoneSegmentation_h + +#include + +#include + +#include "ui_BoneSegmentationControls.h" +#include +#include + +#include +#include +#include +#include +#include + +/** + \brief BoneSegmentation + + \warning The BoneSegmentation plugin provides an automatic bone segmentation for CT images based on deep learning. + At this moment, only CT images can be segmented. The segmentation is done on 2D axial slices. The network was trained + using data from the multiple myeloma project. It was trained on a very limited amount of training data and needs to + be tested in "real world scenarios". + + \sa QmitkAbstractView + \ingroup ${plugin_target}_internal +*/ +class BoneSegmentation : public QmitkAbstractView +{ + // this is needed for all Qt objects that should have a Qt meta-object + // (everything that derives from QObject and wants to have signal/slots) + Q_OBJECT + +public: + static const std::string VIEW_ID; + +signals: + void FinishedSaveTempNrrd(); + +protected slots: + /// \brief Called when the user clicks the GUI button + void OnImageSelectorChanged(); + void DoSaveTempNrrd(); + void DoImageProcessing(); + void DoStartBoneSegmentationProcess(); + void DoBoneSegmentationProcessFinished(); + + void DoLoadTrainedNet(); + +protected: + virtual void CreateQtPartControl(QWidget *parent) override; + virtual void SetFocus() override; + + // Predicate helper + mitk::NodePredicateBase::Pointer GetImagePredicate(); + + Ui::BoneSegmentationControls m_Controls; + mitk::DataNode::ConstPointer m_selectedImageNode = nullptr; + QProcess* m_ProcessBoneSegmentationPython; + QThread m_ThreadSaveTempNrrdImage; + + const QString m_PythonFileName="segment.py"; + const std::string m_TempNrrdFile="temp_CT.nrrd"; + const std::string m_TempBoneSegFile="temp_bone_seg"; +}; + +#endif // BoneSegmentation_h diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/BoneSegmentationControls.ui b/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/BoneSegmentationControls.ui new file mode 100644 index 0000000000..93c6177b65 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/BoneSegmentationControls.ui @@ -0,0 +1,88 @@ + + + BoneSegmentationControls + + + + 0 + 0 + 222 + 161 + + + + + 0 + 0 + + + + QmitkTemplate + + + + + + Please load a trained network! + + + + + + + Load trained network + + + + + + + QLabel { color: rgb(255, 0, 0) } + + + Please select an image! + + + + + + + + + + Do image processing + + + Do Something + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 20 + 220 + + + + + + + + + + QmitkDataStorageComboBoxWithSelectNone + QComboBox +
QmitkDataStorageComboBoxWithSelectNone.h
+
+
+ + +
diff --git a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.cpp b/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/org_mitk_gui_qt_bonesegmentation_Activator.cpp similarity index 60% copy from Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.cpp copy to Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/org_mitk_gui_qt_bonesegmentation_Activator.cpp index 94f25ae860..3688f5646e 100644 --- a/Modules/DiffusionImaging/FiberTracking/Algorithms/TrackingHandlers/mitkTrackingDataHandler.cpp +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/org_mitk_gui_qt_bonesegmentation_Activator.cpp @@ -1,34 +1,29 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#include "mitkTrackingDataHandler.h" + +#include "org_mitk_gui_qt_bonesegmentation_Activator.h" +#include "BoneSegmentation.h" namespace mitk { -TrackingDataHandler::TrackingDataHandler() - : m_AngularThreshold(0.7) - , m_Interpolate(true) - , m_FlipX(false) - , m_FlipY(false) - , m_FlipZ(false) - , m_Mode(MODE::DETERMINISTIC) - , m_RngItk(ItkRngType::New()) - , m_NeedsDataInit(true) - , m_Random(true) -{ + void org_mitk_gui_qt_bonesegmentation_Activator::start(ctkPluginContext *context) + { + BERRY_REGISTER_EXTENSION_CLASS(BoneSegmentation, context) + } -} + void org_mitk_gui_qt_bonesegmentation_Activator::stop(ctkPluginContext *context) { Q_UNUSED(context) } } diff --git a/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/org_mitk_gui_qt_bonesegmentation_Activator.h b/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/org_mitk_gui_qt_bonesegmentation_Activator.h new file mode 100644 index 0000000000..1003010ac9 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.bonesegmentation/src/internal/org_mitk_gui_qt_bonesegmentation_Activator.h @@ -0,0 +1,38 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + + +#ifndef org_mitk_gui_qt_bonesegmentation_Activator_h +#define org_mitk_gui_qt_bonesegmentation_Activator_h + +#include + +namespace mitk +{ + class org_mitk_gui_qt_bonesegmentation_Activator : public QObject, public ctkPluginActivator + { + Q_OBJECT + Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_bonesegmentation") + Q_INTERFACES(ctkPluginActivator) + + public: + void start(ctkPluginContext *context); + void stop(ctkPluginContext *context); + + }; // org_mitk_gui_qt_bonesegmentation_Activator +} + +#endif // org_mitk_gui_qt_bonesegmentation_Activator_h diff --git a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp index 2de9dfc5cd..44d12aaf30 100644 --- a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp @@ -1,822 +1,821 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // itk #include "itksys/SystemTools.hxx" #include #include // Blueberry #include #include // Qmitk #include "QmitkCESTStatisticsView.h" // Qt #include #include // qwt #include // mitk #include #include #include #include #include #include #include #include #include #include #include #include #include // boost #include #include // stl #include #include #include #include #include #include namespace { template void GetSortPermutation(std::vector &out, const std::vector &determiningVector, Compare compare = std::less()) { out.resize(determiningVector.size()); std::iota(out.begin(), out.end(), 0); std::sort(out.begin(), out.end(), [&](unsigned i, unsigned j) { return compare(determiningVector[i], determiningVector[j]); }); } template void ApplyPermutation(const std::vector &order, std::vector &vectorToSort) { assert(order.size() == vectorToSort.size()); std::vector tempVector(vectorToSort.size()); for (unsigned i = 0; i < vectorToSort.size(); i++) { tempVector[i] = vectorToSort[order[i]]; } vectorToSort = tempVector; } template void ApplyPermutation(const std::vector &order, std::vector ¤tVector, std::vector &... remainingVectors) { ApplyPermutation(order, currentVector); ApplyPermutation(order, remainingVectors...); } template void SortVectors(const std::vector &orderDeterminingVector, Compare comparison, std::vector &... vectorsToBeSorted) { std::vector order; GetSortPermutation(order, orderDeterminingVector, comparison); ApplyPermutation(order, vectorsToBeSorted...); } } // namespace const std::string QmitkCESTStatisticsView::VIEW_ID = "org.mitk.views.ceststatistics"; static const int STAT_TABLE_BASE_HEIGHT = 180; QmitkCESTStatisticsView::QmitkCESTStatisticsView(QObject * /*parent*/, const char * /*name*/) { this->m_CalculatorJob = new QmitkImageStatisticsCalculationJob(); m_currentSelectedPosition.Fill(0.0); m_currentSelectedTimeStep = 0; m_CrosshairPointSet = mitk::PointSet::New(); } QmitkCESTStatisticsView::~QmitkCESTStatisticsView() { while (this->m_CalculatorJob->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } delete this->m_CalculatorJob; } void QmitkCESTStatisticsView::SetFocus() { m_Controls.threeDimToFourDimPushButton->setFocus(); } void QmitkCESTStatisticsView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); connect( m_Controls.threeDimToFourDimPushButton, SIGNAL(clicked()), this, SLOT(OnThreeDimToFourDimPushButtonClicked())); connect((QObject *)this->m_CalculatorJob, SIGNAL(finished()), this, SLOT(OnThreadedStatisticsCalculationEnds()), Qt::QueuedConnection); connect((QObject *)(this->m_Controls.fixedRangeCheckBox), SIGNAL(toggled(bool)), (QObject *)this, SLOT(OnFixedRangeCheckBoxToggled(bool))); connect((QObject *)(this->m_Controls.fixedRangeLowerDoubleSpinBox), SIGNAL(editingFinished()), (QObject *)this, SLOT(OnFixedRangeDoubleSpinBoxChanged())); connect((QObject *)(this->m_Controls.fixedRangeUpperDoubleSpinBox), SIGNAL(editingFinished()), (QObject *)this, SLOT(OnFixedRangeDoubleSpinBoxChanged())); m_Controls.threeDimToFourDimPushButton->setEnabled(false); m_Controls.widget_statistics->SetDataStorage(this->GetDataStorage()); this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } void QmitkCESTStatisticsView::RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void QmitkCESTStatisticsView::RenderWindowPartDeactivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void QmitkCESTStatisticsView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList &nodes) { if (nodes.empty()) { std::stringstream message; message << "Please select an image."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } // iterate all selected objects bool atLeastOneWasCESTImage = false; foreach (mitk::DataNode::Pointer node, nodes) { if (node.IsNull()) { continue; } if (dynamic_cast(node->GetData()) != nullptr) { m_Controls.labelWarning->setVisible(false); bool zSpectrumSet = SetZSpectrum(dynamic_cast( node->GetData()->GetProperty(mitk::CustomTagParser::m_OffsetsPropertyName.c_str()).GetPointer())); atLeastOneWasCESTImage = atLeastOneWasCESTImage || zSpectrumSet; if (zSpectrumSet) { m_ZImage = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetImageNodes({node.GetPointer()}); } else { m_MaskImage = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetMaskNodes({node.GetPointer()}); } } if (dynamic_cast(node->GetData()) != nullptr) { m_MaskPlanarFigure = dynamic_cast(node->GetData()); m_Controls.widget_statistics->SetMaskNodes({node.GetPointer()}); } if (dynamic_cast(node->GetData()) != nullptr) { m_PointSet = dynamic_cast(node->GetData()); } } // We only want to offer normalization or timestep copying if one object is selected if (nodes.size() == 1) { if (dynamic_cast(nodes.front()->GetData())) { m_Controls.threeDimToFourDimPushButton->setDisabled(atLeastOneWasCESTImage); } else { m_Controls.threeDimToFourDimPushButton->setEnabled(false); std::stringstream message; message << "The selected node is not an image."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } this->Clear(); return; } // we always need a mask, either image or planar figure as well as an image for further processing if (nodes.size() != 2) { this->Clear(); return; } m_Controls.threeDimToFourDimPushButton->setEnabled(false); if (!atLeastOneWasCESTImage) { std::stringstream message; message << "None of the selected data nodes contains required CEST meta information"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } bool bothAreImages = (m_ZImage.GetPointer() != nullptr) && (m_MaskImage.GetPointer() != nullptr); if (bothAreImages) { bool geometriesMatch = mitk::Equal(*(m_ZImage->GetTimeGeometry()), *(m_MaskImage->GetTimeGeometry()), mitk::eps, false); if (!geometriesMatch) { std::stringstream message; message << "The selected images have different geometries."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } } if (!this->DataSanityCheck()) { this->Clear(); return; } if (m_PointSet.IsNull()) { // initialize thread and trigger it this->m_CalculatorJob->SetIgnoreZeroValueVoxel(false); this->m_CalculatorJob->Initialize(m_ZImage.GetPointer(), m_MaskImage.GetPointer(), m_MaskPlanarFigure.GetPointer()); std::stringstream message; message << "Calculating statistics..."; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); try { // Compute statistics this->m_CalculatorJob->start(); } catch (const mitk::Exception &e) { std::stringstream message; message << "" << e.GetDescription() << ""; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } catch (const std::runtime_error &e) { // In case of exception, print error message on GUI std::stringstream message; message << "" << e.what() << ""; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } catch (const std::exception &e) { MITK_ERROR << "Caught exception: " << e.what(); // In case of exception, print error message on GUI std::stringstream message; message << "Error! Unequal Dimensions of Image and Segmentation. No recompute possible "; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } while (this->m_CalculatorJob->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } } if (m_PointSet.IsNotNull()) { if (m_ZImage->GetDimension() == 4) { AccessFixedDimensionByItk(m_ZImage, PlotPointSet, 4); } else { MITK_WARN << "Expecting a 4D image."; } } } void QmitkCESTStatisticsView::OnThreadedStatisticsCalculationEnds() { this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::xBottom, "delta w"); this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::yLeft, "z"); if (this->m_CalculatorJob->GetStatisticsUpdateSuccessFlag()) { auto statistics = this->m_CalculatorJob->GetStatisticsData(); - auto statisticNonConst = statistics->Clone(); std::string statisticsNodeName = "CEST_statistics"; - auto statisticsNode = mitk::CreateImageStatisticsNode(statisticNonConst, statisticsNodeName); + auto statisticsNode = mitk::CreateImageStatisticsNode(statistics, statisticsNodeName); auto imageRule = mitk::StatisticsToImageRelationRule::New(); - imageRule->Connect(statisticNonConst.GetPointer(), m_CalculatorJob->GetStatisticsImage().GetPointer()); + imageRule->Connect(statistics, m_CalculatorJob->GetStatisticsImage()); if (m_CalculatorJob->GetMaskImage()) { auto maskRule = mitk::StatisticsToMaskRelationRule::New(); - maskRule->Connect(statisticNonConst.GetPointer(), m_CalculatorJob->GetMaskImage().GetPointer()); + maskRule->Connect(statistics, m_CalculatorJob->GetMaskImage()); } else if (m_CalculatorJob->GetPlanarFigure()) { auto planarFigureRule = mitk::StatisticsToMaskRelationRule::New(); - planarFigureRule->Connect(statisticNonConst.GetPointer(), m_CalculatorJob->GetPlanarFigure().GetPointer()); + planarFigureRule->Connect(statistics, m_CalculatorJob->GetPlanarFigure()); } this->GetDataStorage()->Add(statisticsNode); QmitkPlotWidget::DataVector::size_type numberOfSpectra = this->m_zSpectrum.size(); QmitkPlotWidget::DataVector means(numberOfSpectra); QmitkPlotWidget::DataVector stdevs(numberOfSpectra); for (unsigned int index = 0; index < numberOfSpectra; ++index) { means[index] = statistics->GetStatisticsForTimeStep(index).GetValueConverted( mitk::ImageStatisticsConstants::MEAN()); stdevs[index] = statistics->GetStatisticsForTimeStep(index).GetValueConverted( mitk::ImageStatisticsConstants::STANDARDDEVIATION()); } QmitkPlotWidget::DataVector xValues = this->m_zSpectrum; RemoveMZeros(xValues, means, stdevs); ::SortVectors(xValues, std::less(), xValues, means, stdevs); unsigned int curveId = this->m_Controls.m_DataViewWidget->InsertCurve("Spectrum"); this->m_Controls.m_DataViewWidget->SetCurveData(curveId, xValues, means, stdevs, stdevs); this->m_Controls.m_DataViewWidget->SetErrorPen(curveId, QPen(Qt::blue)); QwtSymbol *blueSymbol = new QwtSymbol(QwtSymbol::Rect, QColor(Qt::blue), QColor(Qt::blue), QSize(8, 8)); this->m_Controls.m_DataViewWidget->SetCurveSymbol(curveId, blueSymbol); this->m_Controls.m_DataViewWidget->SetLegendAttribute(curveId, QwtPlotCurve::LegendShowSymbol); QwtLegend *legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); this->m_Controls.m_DataViewWidget->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.m_DataViewWidget->GetPlot() ->axisScaleEngine(QwtPlot::Axis::xBottom) ->setAttributes(QwtScaleEngine::Inverted); this->m_Controls.m_DataViewWidget->Replot(); m_Controls.labelWarning->setVisible(false); m_Controls.m_StatisticsGroupBox->setEnabled(true); m_Controls.m_StatisticsGroupBox->setEnabled(true); if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale( 2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } else { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, true); } } else { m_Controls.labelWarning->setText(m_CalculatorJob->GetLastErrorMessage().c_str()); m_Controls.labelWarning->setVisible(true); this->Clear(); } } void QmitkCESTStatisticsView::OnFixedRangeDoubleSpinBoxChanged() { if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale(2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } this->m_Controls.m_DataViewWidget->Replot(); } template void QmitkCESTStatisticsView::PlotPointSet(itk::Image *image) { this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::xBottom, "delta w"); this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::yLeft, "z"); QmitkPlotWidget::DataVector::size_type numberOfSpectra = this->m_zSpectrum.size(); mitk::PointSet::Pointer internalPointset; if (m_PointSet.IsNotNull()) { internalPointset = m_PointSet; } else { internalPointset = m_CrosshairPointSet; } if (internalPointset.IsNull()) { return; } if (!this->DataSanityCheck()) { m_Controls.labelWarning->setText("Data can not be plotted, internally inconsistent."); m_Controls.labelWarning->show(); return; } auto maxIndex = internalPointset->GetMaxId().Index(); for (std::size_t number = 0; number < maxIndex + 1; ++number) { mitk::PointSet::PointType point; if (!internalPointset->GetPointIfExists(number, &point)) { continue; } if (!this->m_ZImage->GetGeometry()->IsInside(point)) { continue; } itk::Index<3> itkIndex; this->m_ZImage->GetGeometry()->WorldToIndex(point, itkIndex); itk::Index itkIndexTime; itkIndexTime[0] = itkIndex[0]; itkIndexTime[1] = itkIndex[1]; itkIndexTime[2] = itkIndex[2]; QmitkPlotWidget::DataVector values(numberOfSpectra); for (std::size_t step = 0; step < numberOfSpectra; ++step) { if (VImageDimension == 4) { itkIndexTime[3] = step; } values[step] = image->GetPixel(itkIndexTime); } std::stringstream name; name << "Point " << number; // Qcolor enums go from 0 to 19, but 19 is transparent and 0,1 are for bitmaps // 3 is white and thus not visible QColor color(static_cast(number % 17 + 4)); QmitkPlotWidget::DataVector xValues = this->m_zSpectrum; RemoveMZeros(xValues, values); ::SortVectors(xValues, std::less(), xValues, values); unsigned int curveId = this->m_Controls.m_DataViewWidget->InsertCurve(name.str().c_str()); this->m_Controls.m_DataViewWidget->SetCurveData(curveId, xValues, values); this->m_Controls.m_DataViewWidget->SetCurvePen(curveId, QPen(color)); QwtSymbol *symbol = new QwtSymbol(QwtSymbol::Rect, color, color, QSize(8, 8)); this->m_Controls.m_DataViewWidget->SetCurveSymbol(curveId, symbol); this->m_Controls.m_DataViewWidget->SetLegendAttribute(curveId, QwtPlotCurve::LegendShowSymbol); } if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale(2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } else { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, true); } QwtLegend *legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); this->m_Controls.m_DataViewWidget->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.m_DataViewWidget->GetPlot() ->axisScaleEngine(QwtPlot::Axis::xBottom) ->setAttributes(QwtScaleEngine::Inverted); this->m_Controls.m_DataViewWidget->Replot(); m_Controls.labelWarning->setVisible(false); } void QmitkCESTStatisticsView::OnFixedRangeCheckBoxToggled(bool state) { this->m_Controls.fixedRangeLowerDoubleSpinBox->setEnabled(state); this->m_Controls.fixedRangeUpperDoubleSpinBox->setEnabled(state); } void QmitkCESTStatisticsView::RemoveMZeros(QmitkPlotWidget::DataVector &xValues, QmitkPlotWidget::DataVector &yValues) { QmitkPlotWidget::DataVector tempX; QmitkPlotWidget::DataVector tempY; for (std::size_t index = 0; index < xValues.size(); ++index) { if ((xValues.at(index) < -299) || (xValues.at(index)) > 299) { // do not include } else { tempX.push_back(xValues.at(index)); tempY.push_back(yValues.at(index)); } } xValues = tempX; yValues = tempY; } void QmitkCESTStatisticsView::RemoveMZeros(QmitkPlotWidget::DataVector &xValues, QmitkPlotWidget::DataVector &yValues, QmitkPlotWidget::DataVector &stdDevs) { QmitkPlotWidget::DataVector tempX; QmitkPlotWidget::DataVector tempY; QmitkPlotWidget::DataVector tempDevs; for (std::size_t index = 0; index < xValues.size(); ++index) { if ((xValues.at(index) < -299) || (xValues.at(index)) > 299) { // do not include } else { tempX.push_back(xValues.at(index)); tempY.push_back(yValues.at(index)); tempDevs.push_back(stdDevs.at(index)); } } xValues = tempX; yValues = tempY; stdDevs = tempDevs; } void QmitkCESTStatisticsView::OnThreeDimToFourDimPushButtonClicked() { QList nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataNode *node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information(nullptr, "CEST View", "Please load and select an image before starting image processing."); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData *data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image *image = dynamic_cast(data); if (image) { if (image->GetDimension() == 4) { AccessFixedDimensionByItk(image, CopyTimesteps, 4); } this->Clear(); } } } template void QmitkCESTStatisticsView::CopyTimesteps(itk::Image *image) { typedef itk::Image ImageType; // typedef itk::PasteImageFilter PasteImageFilterType; unsigned int numberOfTimesteps = image->GetLargestPossibleRegion().GetSize(3); typename ImageType::RegionType sourceRegion = image->GetLargestPossibleRegion(); sourceRegion.SetSize(3, 1); typename ImageType::RegionType targetRegion = image->GetLargestPossibleRegion(); targetRegion.SetSize(3, 1); for (unsigned int timestep = 1; timestep < numberOfTimesteps; ++timestep) { targetRegion.SetIndex(3, timestep); itk::ImageRegionConstIterator sourceIterator(image, sourceRegion); itk::ImageRegionIterator targetIterator(image, targetRegion); while (!sourceIterator.IsAtEnd()) { targetIterator.Set(sourceIterator.Get()); ++sourceIterator; ++targetIterator; } } } bool QmitkCESTStatisticsView::SetZSpectrum(mitk::StringProperty *zSpectrumProperty) { if (zSpectrumProperty == nullptr) { return false; } mitk::LocaleSwitch localeSwitch("C"); std::string zSpectrumString = zSpectrumProperty->GetValueAsString(); std::istringstream iss(zSpectrumString); std::vector zSpectra; std::copy( std::istream_iterator(iss), std::istream_iterator(), std::back_inserter(zSpectra)); m_zSpectrum.clear(); m_zSpectrum.resize(0); for (auto const &spectrumString : zSpectra) { m_zSpectrum.push_back(std::stod(spectrumString)); } return (m_zSpectrum.size() > 0); } bool QmitkCESTStatisticsView::DataSanityCheck() { QmitkPlotWidget::DataVector::size_type numberOfSpectra = m_zSpectrum.size(); // if we do not have a spectrum, the data can not be processed if (numberOfSpectra == 0) { return false; } // if we do not have CEST image data, the data can not be processed if (m_ZImage.IsNull()) { return false; } // if the CEST image data and the meta information do not match, the data can not be processed if (numberOfSpectra != m_ZImage->GetTimeSteps()) { MITK_INFO << "CEST meta information and number of volumes does not match."; return false; } // if we have neither a mask image, a point set nor a mask planar figure, we can not do statistics // statistics on the whole image would not make sense if (m_MaskImage.IsNull() && m_MaskPlanarFigure.IsNull() && m_PointSet.IsNull() && m_CrosshairPointSet->IsEmpty()) { return false; } // if we have a mask image and a mask planar figure, we can not do statistics // we do not know which one to use if (m_MaskImage.IsNotNull() && m_MaskPlanarFigure.IsNotNull()) { return false; } return true; } void QmitkCESTStatisticsView::Clear() { this->m_zSpectrum.clear(); this->m_zSpectrum.resize(0); this->m_ZImage = nullptr; this->m_MaskImage = nullptr; this->m_MaskPlanarFigure = nullptr; this->m_PointSet = nullptr; this->m_Controls.m_DataViewWidget->Clear(); this->m_Controls.m_StatisticsGroupBox->setEnabled(false); this->m_Controls.widget_statistics->SetImageNodes({}); this->m_Controls.widget_statistics->SetMaskNodes({}); } void QmitkCESTStatisticsView::OnSliceChanged() { mitk::Point3D currentSelectedPosition = this->GetRenderWindowPart()->GetSelectedPosition(nullptr); unsigned int currentSelectedTimeStep = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->GetPos(); if (m_currentSelectedPosition != currentSelectedPosition || m_currentSelectedTimeStep != currentSelectedTimeStep) //|| m_selectedNodeTime > m_currentPositionTime) { // the current position has been changed or the selected node has been changed since the last position validation -> // check position m_currentSelectedPosition = currentSelectedPosition; m_currentSelectedTimeStep = currentSelectedTimeStep; m_currentPositionTime.Modified(); m_CrosshairPointSet->Clear(); m_CrosshairPointSet->SetPoint(0, m_currentSelectedPosition); QList nodes = this->GetDataManagerSelection(); if (nodes.empty() || nodes.size() > 1) return; mitk::DataNode *node = nodes.front(); if (!node) { return; } if (dynamic_cast(node->GetData()) != nullptr) { m_Controls.labelWarning->setVisible(false); bool zSpectrumSet = SetZSpectrum(dynamic_cast( node->GetData()->GetProperty(mitk::CustomTagParser::m_OffsetsPropertyName.c_str()).GetPointer())); if (zSpectrumSet) { m_ZImage = dynamic_cast(node->GetData()); } else { return; } } else { return; } this->m_Controls.m_DataViewWidget->Clear(); AccessFixedDimensionByItk(m_ZImage, PlotPointSet, 4); } } diff --git a/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExample.cpp b/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExample.cpp index ebfa48e94b..092fcde0cb 100644 --- a/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExample.cpp +++ b/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExample.cpp @@ -1,302 +1,329 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "ChartExample.h" // Qt #include const std::string ChartExample::VIEW_ID = "org.mitk.views.chartexample"; void ChartExample::SetFocus() { m_Controls.m_buttonCreateChart->setFocus(); } void ChartExample::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); connect(m_Controls.m_buttonCreateChart, &QPushButton::clicked, this, &ChartExample::CreateChart); connect(m_Controls.m_buttonClearChart, &QPushButton::clicked, this, &ChartExample::ClearChart); connect(m_Controls.m_buttonAddData, &QPushButton::clicked, this, &ChartExample::AddData); connect(m_Controls.m_checkBoxEnableDataX, &QCheckBox::toggled, this, &ChartExample::ShowXData); connect(m_Controls.m_checkBoxEnableErrors, &QCheckBox::toggled, this, &ChartExample::ShowErrorOptions); connect(m_Controls.m_checkBoxEnableXErrors, &QCheckBox::toggled, this, &ChartExample::ShowXErrorOptions); connect(m_Controls.m_checkBoxEnableYErrors, &QCheckBox::toggled, this, &ChartExample::ShowYErrorOptions); + connect(m_Controls.m_doubleSpinBox_minZoomX, &QSpinBox::editingFinished, this, &ChartExample::AdaptZoomX); + connect(m_Controls.m_doubleSpinBox_maxZoomX, &QSpinBox::editingFinished, this, &ChartExample::AdaptZoomX); + connect(m_Controls.m_doubleSpinBox_minZoomY, &QSpinBox::editingFinished, this, &ChartExample::AdaptZoomY); + connect(m_Controls.m_doubleSpinBox_maxZoomY, &QSpinBox::editingFinished, this, &ChartExample::AdaptZoomY); m_Controls.m_groupBoxErrors->setVisible(false); m_Controls.m_groupBoxXErrors->setVisible(false); m_Controls.m_groupBoxYErrors->setVisible(false); m_Controls.m_lineEditDataXVector->setVisible(false); m_Controls.m_lineEditDataXVector->setText("0;1;2;3;4;5;6;7;8;9"); + m_Controls.m_doubleSpinBox_maxZoomX->setValue(10); + m_Controls.m_doubleSpinBox_maxZoomY->setValue(10); + FillRandomDataValues(); auto chartStyle = GetColorTheme(); m_Controls.m_Chart->SetTheme(chartStyle); m_Controls.m_lineEditXAxisLabel->setText("xLabel"); m_Controls.m_lineEditYAxisLabel->setText("yLabel"); m_ChartNameToChartType.emplace("bar", QmitkChartWidget::ChartType::bar); m_ChartNameToChartType.emplace("line", QmitkChartWidget::ChartType::line); m_ChartNameToChartType.emplace("spline", QmitkChartWidget::ChartType::spline); m_ChartNameToChartType.emplace("pie", QmitkChartWidget::ChartType::pie); m_ChartNameToChartType.emplace("area", QmitkChartWidget::ChartType::area); m_ChartNameToChartType.emplace("area-spline", QmitkChartWidget::ChartType::area_spline); m_ChartNameToChartType.emplace("scatter", QmitkChartWidget::ChartType::scatter); m_LineNameToLineType.emplace("solid", QmitkChartWidget::LineStyle::solid); m_LineNameToLineType.emplace("dashed", QmitkChartWidget::LineStyle::dashed); m_AxisScaleNameToAxisScaleType.emplace("linear", QmitkChartWidget::AxisScale::linear); m_AxisScaleNameToAxisScaleType.emplace("logarithmic", QmitkChartWidget::AxisScale::log); + + m_LegendPositionNameToLegendPositionType.emplace("bottom middle", QmitkChartWidget::LegendPosition::bottomMiddle); + m_LegendPositionNameToLegendPositionType.emplace("bottom right", QmitkChartWidget::LegendPosition::bottomRight); + m_LegendPositionNameToLegendPositionType.emplace("top right", QmitkChartWidget::LegendPosition::topRight); + m_LegendPositionNameToLegendPositionType.emplace("top left", QmitkChartWidget::LegendPosition::topLeft); + m_LegendPositionNameToLegendPositionType.emplace("middle right", QmitkChartWidget::LegendPosition::middleRight); } void ChartExample::FillRandomDataValues() { std::vector numbers = GenerateRandomNumbers(10, 10.0); std::string text = ConvertToText(numbers); m_Controls.m_lineEditDataYVector->setText(QString::fromStdString(text)); m_Controls.m_lineEditDataLabel->setText("test" + QString::number(countForUID)); numbers = GenerateRandomNumbers(10, 10.0); text = ConvertToText(numbers); m_Controls.m_lineEditXErrorPlus->setText(QString::fromStdString(text)); numbers = GenerateRandomNumbers(10, 10.0); text = ConvertToText(numbers); m_Controls.m_lineEditXErrorMinus->setText(QString::fromStdString(text)); numbers = GenerateRandomNumbers(10, 10.0); text = ConvertToText(numbers); m_Controls.m_lineEditYErrorPlus->setText(QString::fromStdString(text)); numbers = GenerateRandomNumbers(10, 10.0); text = ConvertToText(numbers); m_Controls.m_lineEditYErrorMinus->setText(QString::fromStdString(text)); countForUID++; } void ChartExample::CreateChart() { auto dataYAxisScaleType = m_AxisScaleNameToAxisScaleType.at(m_Controls.m_comboBoxYAxisScale->currentText().toStdString()); auto xAxisLabel = m_Controls.m_lineEditXAxisLabel->text().toStdString(); auto yAxisLabel = m_Controls.m_lineEditYAxisLabel->text().toStdString(); auto showLegend = m_Controls.m_checkBoxShowLegend->isChecked(); + auto legendPosition = + m_LegendPositionNameToLegendPositionType.at(m_Controls.m_comboBoxLegendPosition->currentText().toStdString()); auto showDataPoints = m_Controls.m_checkBoxShowDataPoints->isChecked(); auto stackedData = m_Controls.m_checkBoxStackedData->isChecked(); auto showSubchart = m_Controls.m_checkBoxShowSubchart->isChecked(); auto title = m_Controls.title->text().toStdString(); m_Controls.m_Chart->SetTitle(title); m_Controls.m_Chart->SetYAxisScale(dataYAxisScaleType); m_Controls.m_Chart->SetXAxisLabel(xAxisLabel); m_Controls.m_Chart->SetYAxisLabel(yAxisLabel); m_Controls.m_Chart->SetShowLegend(showLegend); + m_Controls.m_Chart->SetLegendPosition(legendPosition); m_Controls.m_Chart->SetShowErrorBars(true); m_Controls.m_Chart->SetShowDataPoints(showDataPoints); m_Controls.m_Chart->SetStackedData(stackedData); m_Controls.m_Chart->Show(showSubchart); } void ChartExample::ClearChart() { m_Controls.m_Chart->Clear(); m_Controls.m_plainTextEditDataView->clear(); } std::vector ChartExample::ConvertToVector(const QString &data, QChar delimiter) const { std::vector output; if (data.isEmpty()) { return output; } for (const QString entry : data.split(delimiter)) { output.push_back(entry.toDouble()); } return output; } void ChartExample::AddData() { QString data = m_Controls.m_lineEditDataYVector->text(); auto dataY = ConvertToVector(data); auto chartType = m_ChartNameToChartType.at(m_Controls.m_comboBoxChartType->currentText().toStdString()); std::string dataLabel = m_Controls.m_lineEditDataLabel->text().toStdString(); std::string dataColor = m_Controls.m_lineEditColor->text().toStdString(); auto dataLineStyleType = m_LineNameToLineType.at(m_Controls.m_comboBoxLineStyle->currentText().toStdString()); if (m_Controls.m_checkBoxEnableDataX->isChecked()) { QString lineEditDataX = m_Controls.m_lineEditDataXVector->text(); auto dataX = ConvertToVector(lineEditDataX); if (dataX.size() != dataY.size()) { mitkThrow() << "data x and y size have to be equal"; } auto dataXandY = CreateMap(dataX, dataY); data = QString::fromStdString(ConvertToText(dataXandY)); m_Controls.m_Chart->AddData2D(dataXandY, dataLabel, chartType); } else { m_Controls.m_Chart->AddData1D(dataY, dataLabel, chartType); } if (!dataColor.empty()) { m_Controls.m_Chart->SetColor(dataLabel, dataColor); } if (m_Controls.m_checkBoxEnableErrors->isChecked()) { if (m_Controls.m_checkBoxEnableXErrors->isChecked()) { auto errorsPlus = ConvertToVector(m_Controls.m_lineEditXErrorPlus->text()); auto errorsMinus = ConvertToVector(m_Controls.m_lineEditXErrorMinus->text()); m_Controls.m_Chart->SetXErrorBars(m_Controls.m_lineEditDataLabel->text().toStdString(), errorsPlus, errorsMinus); } if (m_Controls.m_checkBoxEnableYErrors->isChecked()) { auto errorsPlus = ConvertToVector(m_Controls.m_lineEditYErrorPlus->text()); auto errorsMinus = ConvertToVector(m_Controls.m_lineEditYErrorMinus->text()); m_Controls.m_Chart->SetYErrorBars(m_Controls.m_lineEditDataLabel->text().toStdString(), errorsPlus, errorsMinus); } } m_Controls.m_Chart->SetLineStyle(dataLabel, dataLineStyleType); QString dataOverview; dataOverview.append(m_Controls.m_lineEditDataLabel->text()); dataOverview.append("(").append(m_Controls.m_comboBoxChartType->currentText()); if (!dataColor.empty()) { dataOverview.append(", ").append(dataColor.c_str()); } dataOverview.append(", ").append(m_Controls.m_comboBoxLineStyle->currentText()); dataOverview.append(")"); dataOverview.append(":").append(data); m_Controls.m_plainTextEditDataView->appendPlainText(dataOverview); FillRandomDataValues(); } void ChartExample::ShowXData(bool show) { m_Controls.m_lineEditDataXVector->setVisible(show); } void ChartExample::ShowErrorOptions(bool show) { m_Controls.m_groupBoxErrors->setVisible(show); } void ChartExample::ShowXErrorOptions(bool show) { m_Controls.m_groupBoxXErrors->setVisible(show); } void ChartExample::ShowYErrorOptions(bool show) { m_Controls.m_groupBoxYErrors->setVisible(show); } +void ChartExample::AdaptZoomX() { + m_Controls.m_Chart->UpdateMinMaxValueXView(m_Controls.m_doubleSpinBox_minZoomX->value(), + m_Controls.m_doubleSpinBox_maxZoomX->value()); +} + +void ChartExample::AdaptZoomY() +{ + m_Controls.m_Chart->UpdateMinMaxValueYView(m_Controls.m_doubleSpinBox_minZoomY->value(), + m_Controls.m_doubleSpinBox_maxZoomY->value()); +} + std::vector ChartExample::GenerateRandomNumbers(unsigned int amount, double max) const { QRandomGenerator gen; gen.seed(time(NULL)); std::vector data; for (unsigned int i = 0; i < amount; i++) { data.push_back(gen.bounded(max)); } return data; } std::map ChartExample::CreateMap(std::vector keys, std::vector values) const { std::map aMap; std::transform(keys.begin(), keys.end(), values.begin(), std::inserter(aMap, aMap.end()), [](double a, double b) { return std::make_pair(a, b); }); return aMap; } std::string ChartExample::ConvertToText(std::vector numbers, std::string delimiter) const { std::ostringstream oss; oss.precision(3); if (!numbers.empty()) { for (auto number : numbers) { oss << number << delimiter; } } auto aString = oss.str(); aString.pop_back(); return aString; } std::string ChartExample::ConvertToText(std::map numbers, std::string delimiter) const { std::ostringstream oss; oss.precision(3); if (!numbers.empty()) { for (const auto keyValue : numbers) { oss << keyValue.first << ":" << keyValue.second << delimiter; } } auto aString = oss.str(); aString.pop_back(); return aString; } QmitkChartWidget::ColorTheme ChartExample::GetColorTheme() const { ctkPluginContext *context = berry::WorkbenchPlugin::GetDefault()->GetPluginContext(); ctkServiceReference styleManagerRef = context->getServiceReference(); if (styleManagerRef) { auto styleManager = context->getService(styleManagerRef); if (styleManager->GetStyle().name == "Dark") { return QmitkChartWidget::ColorTheme::darkstyle; } else { return QmitkChartWidget::ColorTheme::lightstyle; } } return QmitkChartWidget::ColorTheme::darkstyle; } diff --git a/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExample.h b/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExample.h index 32ae0b67f6..2a717ff41f 100644 --- a/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExample.h +++ b/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExample.h @@ -1,71 +1,75 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef ChartExample_h #define ChartExample_h #include #include "ui_ChartExampleControls.h" /** \brief Basic example for use of module mitkChart \sa QmitkAbstractView \ingroup ${plugin_target}_internal */ class ChartExample : public QmitkAbstractView { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; protected: virtual void CreateQtPartControl(QWidget *parent) override; virtual void SetFocus() override; void CreateChart(); void ClearChart(); void AddData(); void ShowXData(bool show); void ShowErrorOptions(bool show); void ShowXErrorOptions(bool show); void ShowYErrorOptions(bool show); + void AdaptZoomX(); + void AdaptZoomY(); + private: void FillRandomDataValues(); std::vector GenerateRandomNumbers(unsigned int amount, double max) const; std::vector ConvertToVector(const QString& data, QChar delimiter=';') const; std::map CreateMap(std::vector keys, std::vector values) const; std::string ConvertToText(std::vector numbers, std::string delimiter = ";") const; std::string ConvertToText(std::map numbers, std::string delimiter = ";") const; QmitkChartWidget::ColorTheme GetColorTheme() const; std::map m_ChartNameToChartType; std::map m_LineNameToLineType; std::map m_AxisScaleNameToAxisScaleType; + std::map m_LegendPositionNameToLegendPositionType; unsigned int countForUID = 0; Ui::ChartExampleControls m_Controls; }; #endif // ChartExample_h diff --git a/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExampleControls.ui b/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExampleControls.ui index 042618e963..6cbef6d95e 100644 --- a/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExampleControls.ui +++ b/Plugins/org.mitk.gui.qt.chartExample/src/internal/ChartExampleControls.ui @@ -1,566 +1,648 @@ ChartExampleControls 0 0 455 - 1135 + 1054 0 0 QmitkTemplate 0 0 0 0 0 420 - 417 + 411 0 0 Data data entry 0 0 x y: error 0 0 Error values y error x error 0 0 GroupBox plus error minus error 0 0 GroupBox plus error 0 0 0 0 minus error data label 0 0 Chart type 0 0 bar line area spline area-spline scatter Color 0 0 Line style 0 0 solid dashed Add data 0 0 - 437 - 241 + 420 + 236 0 0 Global options QLayout::SetDefaultConstraint QFormLayout::AllNonFixedFieldsGrow Title XAxis label YAxis label Y Axis scale linear logarithmic - + Show legend true - + Stacked data - + Show data points true - + Show Subchart + + + + Legend position + + + + + + + 2 + + + + bottom middle + + + + + bottom right + + + + + top right + + + + + top left + + + + + middle right + + + + + + + Zoom + + + + + 0 + 0 + 431 + 121 + + + + + + + x: + + + + + + + y: + + + + + + + + + + + + + + + + + + Do image processing Create chart Clear chart Qt::Vertical 0 0 0 250 0 0 0 0 0 16777215 150 Qt::Vertical QSizePolicy::Expanding 20 0 QmitkChartWidget QWidget
QmitkChartWidget.h
diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractNodeSelectionWidget.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractNodeSelectionWidget.cpp index 673cdfd067..68c91e55f0 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractNodeSelectionWidget.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractNodeSelectionWidget.cpp @@ -1,113 +1,165 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkAbstractNodeSelectionWidget.h" QmitkAbstractNodeSelectionWidget::QmitkAbstractNodeSelectionWidget(QWidget* parent) : QWidget(parent), m_InvalidInfo("Error. Select data."), m_EmptyInfo("Empty. Make a selection."), m_PopUpTitel("Select a data node"), m_PopUpHint(""), m_IsOptional(false), m_SelectOnlyVisibleNodes(true) { } +QmitkAbstractNodeSelectionWidget::~QmitkAbstractNodeSelectionWidget() +{ + if (!m_DataStorage.IsExpired()) + { + auto dataStorage = m_DataStorage.Lock(); + + // remove Listener for the data storage itself + dataStorage->RemoveObserver(m_DataStorageDeletedTag); + + // remove listener from data storage + dataStorage->RemoveNodeEvent.RemoveListener( + mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeRemovedFromStorage)); + } +}; + + void QmitkAbstractNodeSelectionWidget::SetDataStorage(mitk::DataStorage* dataStorage) { - if (m_DataStorage != dataStorage) + if (m_DataStorage == dataStorage) { - m_DataStorage = dataStorage; - this->OnDataStorageChanged(); - this->UpdateInfo(); + return; } + + if (!m_DataStorage.IsExpired()) + { + auto oldStorage = m_DataStorage.Lock(); + + // remove Listener for the data storage itself + oldStorage->RemoveObserver(m_DataStorageDeletedTag); + + // remove listener from old data storage + oldStorage->RemoveNodeEvent.RemoveListener( + mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeRemovedFromStorage)); + } + + m_DataStorage = dataStorage; + + if (!m_DataStorage.IsExpired()) + { + auto newStorage = m_DataStorage.Lock(); + + // add Listener for the data storage itself + auto command = itk::SimpleMemberCommand::New(); + command->SetCallbackFunction(this, &QmitkAbstractNodeSelectionWidget::SetDataStorageDeleted); + m_DataStorageDeletedTag = newStorage->AddObserver(itk::DeleteEvent(), command); + + // add listener for new data storage + newStorage->RemoveNodeEvent.AddListener( + mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeRemovedFromStorage)); + } + + // update model if the data storage has been changed + m_DataStorage = dataStorage; + this->OnDataStorageChanged(); + this->UpdateInfo(); }; void QmitkAbstractNodeSelectionWidget::SetNodePredicate(mitk::NodePredicateBase* nodePredicate) { if (m_NodePredicate != nodePredicate) { m_NodePredicate = nodePredicate; this->OnNodePredicateChanged(nodePredicate); this->UpdateInfo(); } }; mitk::NodePredicateBase* QmitkAbstractNodeSelectionWidget::GetNodePredicate() const { return m_NodePredicate; } QString QmitkAbstractNodeSelectionWidget::GetInvalidInfo() const { return m_InvalidInfo; }; QString QmitkAbstractNodeSelectionWidget::GetEmptyInfo() const { return m_EmptyInfo; }; QString QmitkAbstractNodeSelectionWidget::GetPopUpTitel() const { return m_PopUpTitel; }; QString QmitkAbstractNodeSelectionWidget::GetPopUpHint() const { return m_PopUpHint; }; bool QmitkAbstractNodeSelectionWidget::GetSelectionIsOptional() const { return m_IsOptional; }; bool QmitkAbstractNodeSelectionWidget::GetSelectOnlyVisibleNodes() const { return m_SelectOnlyVisibleNodes; }; void QmitkAbstractNodeSelectionWidget::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes; }; void QmitkAbstractNodeSelectionWidget::SetInvalidInfo(QString info) { m_InvalidInfo = QString("")+info+QString(""); this->UpdateInfo(); }; void QmitkAbstractNodeSelectionWidget::SetEmptyInfo(QString info) { m_EmptyInfo = QString("")+info+QString(""); this->UpdateInfo(); }; void QmitkAbstractNodeSelectionWidget::SetPopUpTitel(QString info) { m_PopUpTitel = info; }; void QmitkAbstractNodeSelectionWidget::SetPopUpHint(QString info) { m_PopUpHint = info; }; void QmitkAbstractNodeSelectionWidget::SetSelectionIsOptional(bool isOptional) { m_IsOptional = isOptional; this->UpdateInfo(); }; + +void QmitkAbstractNodeSelectionWidget::SetDataStorageDeleted() +{ + this->SetDataStorage(nullptr); +} diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractNodeSelectionWidget.h b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractNodeSelectionWidget.h index 4ec7267eb2..42b1c72a67 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractNodeSelectionWidget.h +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkAbstractNodeSelectionWidget.h @@ -1,151 +1,161 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITK_ABSTRACT_NODE_SELECTION_WIDGET_H #define QMITK_ABSTRACT_NODE_SELECTION_WIDGET_H #include #include #include #include "org_mitk_gui_qt_common_Export.h" #include class QmitkAbstractDataStorageModel; class QAbstractItemVew; /** * \class QmitkAbstractNodeSelectionWidget * \brief Abstract base class for the selection of data from a data storage. */ class MITK_QT_COMMON QmitkAbstractNodeSelectionWidget : public QWidget { Q_OBJECT public: explicit QmitkAbstractNodeSelectionWidget(QWidget* parent = nullptr); + virtual ~QmitkAbstractNodeSelectionWidget(); /** * @brief Sets the data storage that will be used /monitored by widget. * * @par dataStorage A pointer to the data storage to set. */ void SetDataStorage(mitk::DataStorage* dataStorage); /** * Sets the node predicate and updates the widget, according to the node predicate. * Implement OnNodePredicateChange() for custom actualization of a derived widget class. * * @par nodePredicate A pointer to node predicate. */ void SetNodePredicate(mitk::NodePredicateBase* nodePredicate); mitk::NodePredicateBase* GetNodePredicate() const; QString GetInvalidInfo() const; QString GetEmptyInfo() const; QString GetPopUpTitel() const; QString GetPopUpHint() const; bool GetSelectionIsOptional() const; bool GetSelectOnlyVisibleNodes() const; using NodeList = QList; Q_SIGNALS: /* * @brief A signal that will be emitted if the selected node has changed. * * @par nodes A list of data nodes that are newly selected. */ void CurrentSelectionChanged(QList nodes); public Q_SLOTS: /* * @brief Change the selection modus of the item view's selection model. * * If true, an incoming selection will be filtered (reduced) to only those nodes that are visible by the current view. * An outgoing selection can then at most contain the filtered nodes. * If false, the incoming non-visible selection will be stored and later added to the outgoing selection, * to include the original selection that could not be modified. * The part of the original selection, that is non-visible are the nodes that are not * * @par selectOnlyVisibleNodes The bool value to define the selection modus. */ virtual void SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes); /* * @brief Transform a list of data nodes into a model selection and set this as a new selection of the * selection model of the private member item view. * * The function filters the given list of nodes according to the 'm_SelectOnlyVisibleNodes' member variable. If * necessary, the non-visible nodes are stored. This is done if 'm_SelectOnlyVisibleNodes' is false: In this case * the selection may be filtered and only a subset of the selected nodes may be visible and therefore (de-)selectable * in the data storage viewer. By storing the non-visible nodes it is possible to send the new, modified selection * but also include the selected nodes from the original selection that could not be modified (see 'SetSelectOnlyVisibleNodes'). * * @par nodes A list of data nodes that should be newly selected. */ virtual void SetCurrentSelection(NodeList selectedNodes) = 0; /** Set the info text that should be displayed if no (valid) node is selected, * but a selection is mandatory. * The string can contain HTML code. if wanted*/ void SetInvalidInfo(QString info); /** Set the info text that should be displayed if no (valid) node is selected, * but a selection is optional. * The string can contain HTML code. if wanted*/ void SetEmptyInfo(QString info); /** Set the caption of the popup that is displayed to alter the selection. * The string can contain HTML code. if wanted*/ void SetPopUpTitel(QString info); /** Set the hint text of the popup that is displayed to alter the selection. * The string can contain HTML code. if wanted*/ void SetPopUpHint(QString info); /** Set the widget into an optional mode. Optional means that the selection of no valid node does not mean an invalid state. Thus no node is a valid "node" selection too.*/ void SetSelectionIsOptional(bool isOptional); protected: /**Member is called if the display of the selected nodes should be updated.*/ virtual void UpdateInfo() = 0; /**Member is called if the predicate has changed. Thus the selection might change to. The new (changed) predicate is passed with the function call. It is the same like this->GetNodePredicate() called in the function call.*/ virtual void OnNodePredicateChanged(mitk::NodePredicateBase* newPredicate) = 0; /**Member is called if the data storage has changed. Thus the selection might change to.*/ virtual void OnDataStorageChanged() = 0; + virtual void NodeRemovedFromStorage(const mitk::DataNode* node) = 0; + mitk::WeakPointer m_DataStorage; mitk::NodePredicateBase::Pointer m_NodePredicate; QString m_InvalidInfo; QString m_EmptyInfo; QString m_PopUpTitel; QString m_PopUpHint; bool m_IsOptional; bool m_SelectOnlyVisibleNodes; + +private: + /** Helper triggered on the storage delete event */ + void SetDataStorageDeleted(); + + unsigned long m_DataStorageDeletedTag; + }; #endif // QmitkAbstractNodeSelectionWidget_H diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkMultiNodeSelectionWidget.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkMultiNodeSelectionWidget.cpp index 26102b6d3e..ac829d2695 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkMultiNodeSelectionWidget.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkMultiNodeSelectionWidget.cpp @@ -1,244 +1,254 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkMultiNodeSelectionWidget.h" #include #include "QmitkNodeSelectionDialog.h" #include "QmitkCustomVariants.h" #include "internal/QmitkNodeSelectionListItemWidget.h" QmitkMultiNodeSelectionWidget::QmitkMultiNodeSelectionWidget(QWidget* parent) : QmitkAbstractNodeSelectionWidget(parent) { m_Controls.setupUi(this); m_Overlay = new QmitkSimpleTextOverlayWidget(m_Controls.list); m_Overlay->setVisible(false); m_CheckFunction = [](const NodeList &) { return ""; }; this->UpdateList(); this->UpdateInfo(); connect(m_Controls.btnChange, SIGNAL(clicked(bool)), this, SLOT(OnEditSelection())); } QmitkMultiNodeSelectionWidget::NodeList QmitkMultiNodeSelectionWidget::CompileEmitSelection() const { NodeList result; for (int i = 0; i < m_Controls.list->count(); ++i) { QListWidgetItem* item = m_Controls.list->item(i); auto node = item->data(Qt::UserRole).value(); result.append(node); } if (!m_SelectOnlyVisibleNodes) { for (auto node : m_CurrentSelection) { if (!result.contains(node)) { result.append(node); } } } return result; } void QmitkMultiNodeSelectionWidget::OnNodePredicateChanged(mitk::NodePredicateBase* /*newPredicate*/) { this->UpdateInfo(); this->UpdateList(); }; void QmitkMultiNodeSelectionWidget::OnDataStorageChanged() { this->UpdateInfo(); this->UpdateList(); }; QmitkMultiNodeSelectionWidget::NodeList QmitkMultiNodeSelectionWidget::GetSelectedNodes() const { return m_CurrentSelection; }; void QmitkMultiNodeSelectionWidget::SetSelectionCheckFunction(const SelectionCheckFunctionType &checkFunction) { m_CheckFunction = checkFunction; auto newEmission = this->CompileEmitSelection(); auto newCheckResponse = m_CheckFunction(newEmission); if (newCheckResponse.empty() && !m_CheckResponse.empty()) { emit CurrentSelectionChanged(newEmission); } m_CheckResponse = newCheckResponse; this->UpdateInfo(); }; void QmitkMultiNodeSelectionWidget::OnEditSelection() { QmitkNodeSelectionDialog* dialog = new QmitkNodeSelectionDialog(this, m_PopUpTitel, m_PopUpHint); dialog->SetDataStorage(m_DataStorage.Lock()); dialog->SetNodePredicate(m_NodePredicate); dialog->SetCurrentSelection(this->CompileEmitSelection()); dialog->SetSelectOnlyVisibleNodes(m_SelectOnlyVisibleNodes); dialog->SetSelectionMode(QAbstractItemView::MultiSelection); m_Controls.btnChange->setChecked(true); if (dialog->exec()) { auto lastEmission = this->CompileEmitSelection(); m_CurrentSelection = dialog->GetSelectedNodes(); this->UpdateList(); auto newEmission = this->CompileEmitSelection(); m_CheckResponse = m_CheckFunction(newEmission); this->UpdateInfo(); if (!EqualNodeSelections(lastEmission, newEmission)) { if (m_CheckResponse.empty()) { emit CurrentSelectionChanged(newEmission); } } } m_Controls.btnChange->setChecked(false); delete dialog; }; void QmitkMultiNodeSelectionWidget::UpdateInfo() { if (!m_Controls.list->count()) { if (m_IsOptional) { m_Overlay->SetOverlayText(m_EmptyInfo); } else { m_Overlay->SetOverlayText(m_InvalidInfo); } } else { if (!m_CheckResponse.empty()) { m_Overlay->SetOverlayText(QString::fromStdString(m_CheckResponse)); } } m_Overlay->setVisible(m_Controls.list->count() == 0 || !m_CheckResponse.empty()); for (auto i = 0; i < m_Controls.list->count(); ++i) { auto item = m_Controls.list->item(i); auto widget = qobject_cast(m_Controls.list->itemWidget(item)); widget->SetClearAllowed(m_IsOptional || m_CurrentSelection.size() > 1); } }; void QmitkMultiNodeSelectionWidget::UpdateList() { m_Controls.list->clear(); for (auto node : m_CurrentSelection) { if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node)) { QListWidgetItem *newItem = new QListWidgetItem; newItem->setSizeHint(QSize(0, 40)); QmitkNodeSelectionListItemWidget* widget = new QmitkNodeSelectionListItemWidget; widget->SetSelectedNode(node); widget->SetClearAllowed(m_IsOptional || m_CurrentSelection.size() > 1); - connect(widget, SIGNAL(ClearSelection(mitk::DataNode*)), this, SLOT(OnClearSelection(mitk::DataNode*))); + connect(widget, &QmitkNodeSelectionListItemWidget::ClearSelection, this, &QmitkMultiNodeSelectionWidget::OnClearSelection); newItem->setData(Qt::UserRole, QVariant::fromValue(node)); m_Controls.list->addItem(newItem); m_Controls.list->setItemWidget(newItem, widget); } } }; void QmitkMultiNodeSelectionWidget::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { auto lastEmission = this->CompileEmitSelection(); m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes; auto newEmission = this->CompileEmitSelection(); if (!EqualNodeSelections(lastEmission, newEmission)) { m_CheckResponse = m_CheckFunction(newEmission); if (m_CheckResponse.empty()) { emit CurrentSelectionChanged(newEmission); } this->UpdateList(); this->UpdateInfo(); } }; void QmitkMultiNodeSelectionWidget::SetCurrentSelection(NodeList selectedNodes) { auto lastEmission = this->CompileEmitSelection(); m_CurrentSelection = selectedNodes; this->UpdateList(); auto newEmission = this->CompileEmitSelection(); if (!EqualNodeSelections(lastEmission, newEmission)) { m_CheckResponse = m_CheckFunction(newEmission); if (m_CheckResponse.empty()) { emit CurrentSelectionChanged(newEmission); } this->UpdateInfo(); } }; -void QmitkMultiNodeSelectionWidget::OnClearSelection(mitk::DataNode* node) +void QmitkMultiNodeSelectionWidget::OnClearSelection(const mitk::DataNode* node) { auto finding = std::find(std::begin(m_CurrentSelection), std::end(m_CurrentSelection), node); m_CurrentSelection.erase(finding); this->UpdateList(); auto newEmission = this->CompileEmitSelection(); m_CheckResponse = m_CheckFunction(newEmission); if (m_CheckResponse.empty()) { emit CurrentSelectionChanged(newEmission); } this->UpdateInfo(); }; + +void QmitkMultiNodeSelectionWidget::NodeRemovedFromStorage(const mitk::DataNode* node) +{ + auto finding = std::find(std::begin(m_CurrentSelection), std::end(m_CurrentSelection), node); + + if (finding != std::end(m_CurrentSelection)) + { + this->OnClearSelection(node); + } +} \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkMultiNodeSelectionWidget.h b/Plugins/org.mitk.gui.qt.common/src/QmitkMultiNodeSelectionWidget.h index 28cc17ed33..c3c3a23141 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkMultiNodeSelectionWidget.h +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkMultiNodeSelectionWidget.h @@ -1,88 +1,89 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITK_MULTI_NODE_SELECTION_WIDGET_H #define QMITK_MULTI_NODE_SELECTION_WIDGET_H #include #include #include #include "QmitkSimpleTextOverlayWidget.h" #include "org_mitk_gui_qt_common_Export.h" #include "ui_QmitkMultiNodeSelectionWidget.h" #include class QmitkAbstractDataStorageModel; class QAbstractItemVew; /** * \class QmitkMultiNodeSelectionWidget * \brief Widget that allows to perform and represents a multiple node selection. */ class MITK_QT_COMMON QmitkMultiNodeSelectionWidget : public QmitkAbstractNodeSelectionWidget { Q_OBJECT public: explicit QmitkMultiNodeSelectionWidget(QWidget* parent = nullptr); using NodeList = QmitkAbstractNodeSelectionWidget::NodeList; NodeList GetSelectedNodes() const; /**Helper function that is used to check the given selection for consistency. Returning an empty string assumes that everything is alright and the selection is valid. If the string is not empty, the content of the string will be used as error message in the overlay to indicate the problem.*/ using SelectionCheckFunctionType = std::function; /**A selection check function can be set. If set the widget uses this function to check the made/set selection. If the selection is valid, everything is fine. If selection is indicated as invalid, it will not be communicated by the widget (no signal emission.*/ void SetSelectionCheckFunction(const SelectionCheckFunctionType &checkFunction); public Q_SLOTS: virtual void SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) override; virtual void SetCurrentSelection(NodeList selectedNodes) override; void OnEditSelection(); protected Q_SLOTS: - void OnClearSelection(mitk::DataNode* node); + void OnClearSelection(const mitk::DataNode* node); protected: NodeList CompileEmitSelection() const; - virtual void UpdateInfo() override; + void UpdateInfo() override; virtual void UpdateList(); - virtual void OnNodePredicateChanged(mitk::NodePredicateBase* newPredicate) override; - virtual void OnDataStorageChanged() override; + void OnNodePredicateChanged(mitk::NodePredicateBase* newPredicate) override; + void OnDataStorageChanged() override; + void NodeRemovedFromStorage(const mitk::DataNode* node) override; NodeList m_CurrentSelection; QmitkSimpleTextOverlayWidget* m_Overlay; SelectionCheckFunctionType m_CheckFunction; std::string m_CheckResponse; Ui_QmitkMultiNodeSelectionWidget m_Controls; }; #endif // QmitkMultiNodeSelectionWidget_H diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionButton.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionButton.cpp index 08bb6248cf..981651861d 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionButton.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionButton.cpp @@ -1,181 +1,181 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkNodeSelectionButton.h" // berry includes #include #include #include "QPainter" #include "QTextDocument" #include #include // mitk core #include #include #include #include #include #include // vtk #include QPixmap GetPixmapFromImageNode(const mitk::DataNode* dataNode, int height) { if (nullptr == dataNode) { return QPixmap(); } const mitk::Image* image = dynamic_cast(dataNode->GetData()); if ((nullptr == image || !image->IsInitialized()) || // -> must be an image (image->GetPixelType().GetNumberOfComponents() != 1)) // -> for now only single component are allowed { auto descManager = QmitkNodeDescriptorManager::GetInstance(); auto desc = descManager->GetDescriptor(dataNode); auto icon = desc->GetIcon(dataNode); auto fallBackMap = icon.pixmap(height, height); return fallBackMap; } mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); int sliceNumber = image->GetDimension(2) / 2; planeGeometry->InitializeStandardPlane(image->GetGeometry(), mitk::PlaneGeometry::Axial, sliceNumber); mitk::ExtractSliceFilter::Pointer extractSliceFilter = mitk::ExtractSliceFilter::New(); extractSliceFilter->SetInput(image); extractSliceFilter->SetInterpolationMode(mitk::ExtractSliceFilter::RESLICE_CUBIC); extractSliceFilter->SetResliceTransformByGeometry(image->GetGeometry()); extractSliceFilter->SetWorldGeometry(planeGeometry); extractSliceFilter->SetOutputDimensionality(2); extractSliceFilter->SetVtkOutputRequest(true); extractSliceFilter->Update(); vtkImageData* imageData = extractSliceFilter->GetVtkOutput(); mitk::LevelWindow levelWindow; dataNode->GetLevelWindow(levelWindow); vtkSmartPointer lookupTable = vtkSmartPointer::New(); lookupTable->SetRange(levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound()); lookupTable->SetSaturationRange(0.0, 0.0); lookupTable->SetValueRange(0.0, 1.0); lookupTable->SetHueRange(0.0, 0.0); lookupTable->SetRampToLinear(); vtkSmartPointer levelWindowFilter = vtkSmartPointer::New(); levelWindowFilter->SetLookupTable(lookupTable); levelWindowFilter->SetInputData(imageData); levelWindowFilter->SetMinOpacity(0.0); levelWindowFilter->SetMaxOpacity(1.0); int dims[3]; imageData->GetDimensions(dims); double clippingBounds[] = { 0.0, static_cast(dims[0]), 0.0, static_cast(dims[1]) }; levelWindowFilter->SetClippingBounds(clippingBounds); levelWindowFilter->Update(); imageData = levelWindowFilter->GetOutput(); QImage thumbnailImage(reinterpret_cast(imageData->GetScalarPointer()), dims[0], dims[1], QImage::Format_ARGB32); thumbnailImage = thumbnailImage.scaledToHeight(height,Qt::SmoothTransformation).rgbSwapped(); return QPixmap::fromImage(thumbnailImage); } QmitkNodeSelectionButton::QmitkNodeSelectionButton(QWidget *parent) : QPushButton(parent), m_OutDatedThumpNail(true) { } QmitkNodeSelectionButton::~QmitkNodeSelectionButton() { this->m_SelectedNode = nullptr; } -mitk::DataNode::Pointer QmitkNodeSelectionButton::GetSelectedNode() const +const mitk::DataNode* QmitkNodeSelectionButton::GetSelectedNode() const { return m_SelectedNode; } -void QmitkNodeSelectionButton::SetSelectedNode(mitk::DataNode* node) +void QmitkNodeSelectionButton::SetSelectedNode(const mitk::DataNode* node) { if (m_SelectedNode != node) { this->m_SelectedNode = node; this->m_OutDatedThumpNail = true; } this->update(); }; void QmitkNodeSelectionButton::SetNodeInfo(QString info) { this->m_Info = info; this->update(); }; void QmitkNodeSelectionButton::paintEvent(QPaintEvent *p) { QString stylesheet; ctkPluginContext* context = berry::WorkbenchPlugin::GetDefault()->GetPluginContext(); ctkServiceReference styleManagerRef = context->getServiceReference(); if (styleManagerRef) { auto styleManager = context->getService(styleManagerRef); stylesheet = styleManager->GetStylesheet(); } QPushButton::paintEvent(p); QPainter painter(this); QTextDocument td(this); td.setDefaultStyleSheet(stylesheet); auto widgetSize = this->size(); QPoint origin = QPoint(5, 5); if (this->m_SelectedNode) { auto iconLength = widgetSize.height() - 10; auto node = this->m_SelectedNode; if (this->m_OutDatedThumpNail) { this->m_ThumpNail = GetPixmapFromImageNode(node, iconLength); this->m_OutDatedThumpNail = false; } painter.drawPixmap(origin, m_ThumpNail); origin.setX(origin.x() + iconLength + 5); td.setHtml(QString::fromStdString(""+node->GetName()+"")); } else { td.setHtml(m_Info); } auto textSize = td.size(); origin.setY( (widgetSize.height() - textSize.height()) / 2.); painter.translate(origin); td.drawContents(&painter); } diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionButton.h b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionButton.h index a8d78302ce..8d7b63ec38 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionButton.h +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkNodeSelectionButton.h @@ -1,58 +1,58 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITK_NODE_SELECTION_BUTTON_H #define QMITK_NODE_SELECTION_BUTTON_H #include #include #include "org_mitk_gui_qt_common_Export.h" #include "QPushButton" #include "QPixmap" /** Button class that can be used to display informations about a passed node. * If the passed node is a null ptr the node info text will be shown. * In difference to the normal push button text property. The node info can * be formated text (e.g. HTML code; like the tooltip text).*/ class MITK_QT_COMMON QmitkNodeSelectionButton : public QPushButton { Q_OBJECT public: explicit QmitkNodeSelectionButton(QWidget *parent = nullptr); ~QmitkNodeSelectionButton(); - mitk::DataNode::Pointer GetSelectedNode() const; + const mitk::DataNode* GetSelectedNode() const; public Q_SLOTS : - virtual void SetSelectedNode(mitk::DataNode* node); + virtual void SetSelectedNode(const mitk::DataNode* node); virtual void SetNodeInfo(QString info); protected: void paintEvent(QPaintEvent *p) override; - mitk::DataNode::Pointer m_SelectedNode; + mitk::DataNode::ConstPointer m_SelectedNode; QString m_Info; bool m_OutDatedThumpNail; QPixmap m_ThumpNail; }; #endif // QmitkSingleNodeSelectionWidget_H diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.cpp index 2ede8b2f0b..737924a397 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.cpp @@ -1,237 +1,249 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkSingleNodeSelectionWidget.h" #include #include #include "QmitkNodeSelectionDialog.h" #include "QmitkNodeDetailsDialog.h" QmitkSingleNodeSelectionWidget::QmitkSingleNodeSelectionWidget(QWidget* parent) : QmitkAbstractNodeSelectionWidget(parent) { m_Controls.setupUi(this); m_Controls.btnSelect->installEventFilter(this); m_Controls.btnSelect->setVisible(true); m_Controls.btnClear->setVisible(false); m_Controls.btnClear->setIcon(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org.mitk.gui.qt.common/times.svg"))); this->UpdateInfo(); connect(m_Controls.btnClear, SIGNAL(clicked(bool)), this, SLOT(OnClearSelection())); } QmitkSingleNodeSelectionWidget::~QmitkSingleNodeSelectionWidget() { } mitk::DataNode::Pointer QmitkSingleNodeSelectionWidget::ExtractCurrentValidSelection(const NodeList& nodes) const { mitk::DataNode::Pointer result = nullptr; for (auto node : nodes) { bool valid = true; if (m_NodePredicate.IsNotNull()) { valid = m_NodePredicate->CheckNode(node); } if (valid) { result = node; break; } } return result; } QmitkSingleNodeSelectionWidget::NodeList QmitkSingleNodeSelectionWidget::CompileEmitSelection() const { NodeList result; if (!m_SelectOnlyVisibleNodes) { result = m_ExternalSelection; } if (m_SelectedNode.IsNotNull() && !result.contains(m_SelectedNode)) { result.append(m_SelectedNode); } return result; } void QmitkSingleNodeSelectionWidget::OnNodePredicateChanged(mitk::NodePredicateBase* /*newPredicate*/) { m_SelectedNode = this->ExtractCurrentValidSelection(m_ExternalSelection); }; void QmitkSingleNodeSelectionWidget::OnDataStorageChanged() { }; void QmitkSingleNodeSelectionWidget::OnClearSelection() { if (m_IsOptional) { NodeList emptyList; this->SetCurrentSelection(emptyList); } this->UpdateInfo(); } mitk::DataNode::Pointer QmitkSingleNodeSelectionWidget::GetSelectedNode() const { return m_SelectedNode; }; bool QmitkSingleNodeSelectionWidget::eventFilter(QObject *obj, QEvent *ev) { if (obj == m_Controls.btnSelect) { if (ev->type() == QEvent::MouseButtonRelease) { auto mouseEv = dynamic_cast(ev); if (!mouseEv) { return false; } if (mouseEv->button() == Qt::LeftButton) { this->EditSelection(); return true; } else { auto selection = this->CompileEmitSelection(); if (!selection.empty()) { QmitkNodeDetailsDialog infoDialog(selection, this); infoDialog.exec(); return true; } } } } return false; } void QmitkSingleNodeSelectionWidget::EditSelection() { QmitkNodeSelectionDialog* dialog = new QmitkNodeSelectionDialog(this, m_PopUpTitel, m_PopUpHint); dialog->SetDataStorage(m_DataStorage.Lock()); dialog->SetNodePredicate(m_NodePredicate); NodeList list; if (m_SelectedNode.IsNotNull()) { list.append(m_SelectedNode); } dialog->SetCurrentSelection(list); dialog->SetSelectOnlyVisibleNodes(m_SelectOnlyVisibleNodes); dialog->SetSelectionMode(QAbstractItemView::SingleSelection); m_Controls.btnSelect->setChecked(true); if (dialog->exec()) { auto lastEmission = this->CompileEmitSelection(); auto nodes = dialog->GetSelectedNodes(); if (nodes.empty()) { m_SelectedNode = nullptr; } else { m_SelectedNode = nodes.first(); } auto newEmission = this->CompileEmitSelection(); if (!EqualNodeSelections(lastEmission, newEmission)) { emit CurrentSelectionChanged(newEmission); this->UpdateInfo(); } } m_Controls.btnSelect->setChecked(false); delete dialog; }; void QmitkSingleNodeSelectionWidget::UpdateInfo() { if (m_SelectedNode.IsNull()) { if (m_IsOptional) { m_Controls.btnSelect->SetNodeInfo(m_EmptyInfo); } else { m_Controls.btnSelect->SetNodeInfo(m_InvalidInfo); } m_Controls.btnClear->setVisible(false); } else { m_Controls.btnClear->setVisible(m_IsOptional); } m_Controls.btnSelect->SetSelectedNode(m_SelectedNode); }; void QmitkSingleNodeSelectionWidget::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { auto lastEmission = this->CompileEmitSelection(); m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes; auto newEmission = this->CompileEmitSelection(); if (!EqualNodeSelections(lastEmission, newEmission)) { emit CurrentSelectionChanged(newEmission); this->UpdateInfo(); } }; void QmitkSingleNodeSelectionWidget::SetCurrentSelection(NodeList selectedNodes) { auto lastEmission = this->CompileEmitSelection(); m_ExternalSelection = selectedNodes; m_SelectedNode = this->ExtractCurrentValidSelection(selectedNodes); auto newEmission = this->CompileEmitSelection(); if (!EqualNodeSelections(lastEmission, newEmission)) { this->UpdateInfo(); emit CurrentSelectionChanged(newEmission); } }; + +void QmitkSingleNodeSelectionWidget::NodeRemovedFromStorage(const mitk::DataNode* node) +{ + if (m_SelectedNode == node && node != nullptr) + { + m_SelectedNode = nullptr; + auto newEmission = this->CompileEmitSelection(); + + emit CurrentSelectionChanged(newEmission); + this->UpdateInfo(); + } +} \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.h b/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.h index 4f67d11568..3cb7f8f079 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.h +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkSingleNodeSelectionWidget.h @@ -1,76 +1,76 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITK_SINGLE_NODE_SELECTION_WIDGET_H #define QMITK_SINGLE_NODE_SELECTION_WIDGET_H #include #include #include #include "org_mitk_gui_qt_common_Export.h" #include "ui_QmitkSingleNodeSelectionWidget.h" #include #include class QmitkAbstractDataStorageModel; /** * \class QmitkSingleNodeSelectionWidget * \brief Widget that represents a node selection. It acts like a button. Clicking on it * allows to change the selection. */ class MITK_QT_COMMON QmitkSingleNodeSelectionWidget : public QmitkAbstractNodeSelectionWidget { Q_OBJECT public: explicit QmitkSingleNodeSelectionWidget(QWidget* parent = nullptr); ~QmitkSingleNodeSelectionWidget(); mitk::DataNode::Pointer GetSelectedNode() const; using NodeList = QmitkAbstractNodeSelectionWidget::NodeList; public Q_SLOTS: virtual void SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) override; virtual void SetCurrentSelection(NodeList selectedNodes) override; protected Q_SLOTS: virtual void OnClearSelection(); protected: mitk::DataNode::Pointer ExtractCurrentValidSelection(const NodeList& nodes) const; NodeList CompileEmitSelection() const; - virtual bool eventFilter(QObject *obj, QEvent *ev) override; + bool eventFilter(QObject *obj, QEvent *ev) override; void EditSelection(); - virtual void UpdateInfo() override; - - virtual void OnNodePredicateChanged(mitk::NodePredicateBase* newPredicate) override; - virtual void OnDataStorageChanged() override; + void UpdateInfo() override; + void OnNodePredicateChanged(mitk::NodePredicateBase* newPredicate) override; + void OnDataStorageChanged() override; + void NodeRemovedFromStorage(const mitk::DataNode* node) override; NodeList m_ExternalSelection; mitk::DataNode::Pointer m_SelectedNode; Ui_QmitkSingleNodeSelectionWidget m_Controls; }; #endif // QmitkSingleNodeSelectionWidget_H diff --git a/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionListItemWidget.cpp b/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionListItemWidget.cpp index cba2d8d983..4f3c142955 100644 --- a/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionListItemWidget.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionListItemWidget.cpp @@ -1,94 +1,94 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkNodeSelectionListItemWidget.h" #include #include #include "QmitkNodeDetailsDialog.h" QmitkNodeSelectionListItemWidget::QmitkNodeSelectionListItemWidget(QWidget *parent) : QWidget(parent) { m_Controls.setupUi(this); m_Controls.btnSelect->installEventFilter(this); m_Controls.btnSelect->setVisible(true); m_Controls.btnSelect->SetNodeInfo("No valid selection"); m_Controls.btnClear->setVisible(false); m_Controls.btnClear->setIcon(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org.mitk.gui.qt.common/times.svg"))); connect(m_Controls.btnClear, SIGNAL(clicked(bool)), this, SLOT(OnClearSelection())); } QmitkNodeSelectionListItemWidget::~QmitkNodeSelectionListItemWidget() { } -mitk::DataNode::Pointer QmitkNodeSelectionListItemWidget::GetSelectedNode() const +const mitk::DataNode* QmitkNodeSelectionListItemWidget::GetSelectedNode() const { return m_Controls.btnSelect->GetSelectedNode(); }; -void QmitkNodeSelectionListItemWidget::SetSelectedNode(mitk::DataNode* node) +void QmitkNodeSelectionListItemWidget::SetSelectedNode(const mitk::DataNode* node) { m_Controls.btnSelect->SetSelectedNode(node); this->update(); }; void QmitkNodeSelectionListItemWidget::SetClearAllowed(bool allowed) { m_Controls.btnClear->setVisible(allowed); }; void QmitkNodeSelectionListItemWidget::OnClearSelection() { emit ClearSelection(this->GetSelectedNode()); }; bool QmitkNodeSelectionListItemWidget::eventFilter(QObject *obj, QEvent *ev) { if (obj == m_Controls.btnSelect) { if (ev->type() == QEvent::MouseButtonRelease) { auto mouseEv = dynamic_cast(ev); if (!mouseEv) { return false; } if (mouseEv->button() == Qt::RightButton) { auto selection = this->GetSelectedNode(); - if (selection.IsNotNull()) + if (selection != nullptr) { - QList selectionList({ this->GetSelectedNode() }); + QList selectionList({ this->GetSelectedNode() }); QmitkNodeDetailsDialog infoDialog(selectionList, this); infoDialog.exec(); return true; } } } } return false; } diff --git a/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionListItemWidget.h b/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionListItemWidget.h index 05c7b22696..46f2acdd10 100644 --- a/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionListItemWidget.h +++ b/Plugins/org.mitk.gui.qt.common/src/internal/QmitkNodeSelectionListItemWidget.h @@ -1,56 +1,56 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QMITK_NODE_SELECTION_LIST_ITEM_WIDGET_H #define QMITK_NODE_SELECTION_LIST_ITEM_WIDGET_H #include #include #include "ui_QmitkNodeSelectionListItemWidget.h" #include "org_mitk_gui_qt_common_Export.h" class MITK_QT_COMMON QmitkNodeSelectionListItemWidget : public QWidget { Q_OBJECT public: explicit QmitkNodeSelectionListItemWidget(QWidget* parent = nullptr); ~QmitkNodeSelectionListItemWidget(); - mitk::DataNode::Pointer GetSelectedNode() const; + const mitk::DataNode* GetSelectedNode() const; public Q_SLOTS : - virtual void SetSelectedNode(mitk::DataNode* node); + virtual void SetSelectedNode(const mitk::DataNode* node); virtual void SetClearAllowed(bool allowed); signals: - void ClearSelection(mitk::DataNode* node); + void ClearSelection(const mitk::DataNode* node); protected Q_SLOTS: void OnClearSelection(); protected: virtual bool eventFilter(QObject *obj, QEvent *ev) override; Ui_QmitkNodeSelectionListItemWidget m_Controls; }; #endif // QMITK_NODE_SELECTION_LIST_ITEM_WIDGET_H diff --git a/Plugins/org.mitk.gui.qt.datastorageviewertest/src/internal/QmitkDataStorageViewerTestView.cpp b/Plugins/org.mitk.gui.qt.datastorageviewertest/src/internal/QmitkDataStorageViewerTestView.cpp index 3fe521c862..902d116470 100644 --- a/Plugins/org.mitk.gui.qt.datastorageviewertest/src/internal/QmitkDataStorageViewerTestView.cpp +++ b/Plugins/org.mitk.gui.qt.datastorageviewertest/src/internal/QmitkDataStorageViewerTestView.cpp @@ -1,278 +1,278 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical Image Computing. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // data storage viewer test plugin #include "QmitkDataStorageViewerTestView.h" #include "mitkNodePredicateDataType.h" // berry #include // qt #include const std::string QmitkDataStorageViewerTestView::VIEW_ID = "org.mitk.views.datastorageviewertest"; void QmitkDataStorageViewerTestView::SetFocus() { // nothing here } void QmitkDataStorageViewerTestView::CreateQtPartControl(QWidget* parent) { // create GUI widgets m_Controls.setupUi(parent); m_DataStorageDefaultListModel = new QmitkDataStorageDefaultListModel(this); m_DataStorageDefaultListModel->SetDataStorage(GetDataStorage()); m_Controls.selectionListView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_Controls.selectionListView->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.selectionListView->setAlternatingRowColors(true); m_Controls.selectionListView->setModel(m_DataStorageDefaultListModel); m_DataStorageDefaultListModel2 = new QmitkDataStorageDefaultListModel(this); m_DataStorageDefaultListModel2->SetDataStorage(GetDataStorage()); m_Controls.selectionListView2->setSelectionMode(QAbstractItemView::ExtendedSelection); m_Controls.selectionListView2->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.selectionListView2->setAlternatingRowColors(true); m_Controls.selectionListView2->setModel(m_DataStorageDefaultListModel2); m_Controls.singleSlot->SetDataStorage(GetDataStorage()); m_Controls.singleSlot->SetEmptyInfo(QString("EmptyInfo: Set this to display info in empty state")); m_Controls.singleSlot->SetInvalidInfo(QString("InvalidInfo: is displayed for invalid states")); m_Controls.singleSlot->SetPopUpTitel(QString("This is the definable caption. Choose your data now!")); m_Controls.singleSlot->SetPopUpHint(QString("I am an optional hint, that can be set by the developer

If not set the widget is invisible.")); m_Controls.multiSlot->SetDataStorage(GetDataStorage()); m_Controls.multiSlot->SetEmptyInfo(QString("EmptyInfo: Set this to display info in empty state")); m_Controls.multiSlot->SetInvalidInfo(QString("InvalidInfo: is displayed for invalid states")); m_Controls.multiSlot->SetPopUpTitel(QString("This is the definable caption. Choose your data now!")); m_Controls.multiSlot->SetPopUpHint(QString("I am an optional hint, that can be set by the developer

If not set the widget is invisible.")); m_ModelViewSelectionConnector = std::make_unique(); try { m_ModelViewSelectionConnector->SetView(m_Controls.selectionListView); } catch (mitk::Exception& e) { mitkReThrow(e) << "Cannot connect the model-view pair signals and slots."; } m_SelectionServiceConnector = std::make_unique(); m_ModelViewSelectionConnector2 = std::make_unique(); try { m_ModelViewSelectionConnector2->SetView(m_Controls.selectionListView2); } catch (mitk::Exception& e) { mitkReThrow(e) << "Cannot connect the model-view pair signals and slots."; } m_SelectionServiceConnector2 = std::make_unique(); m_SelectionServiceConnector3 = std::make_unique(); m_SelectionServiceConnector4 = std::make_unique(); connect(m_Controls.selectionProviderCheckBox, SIGNAL(toggled(bool)), this, SLOT(SetAsSelectionProvider1(bool))); connect(m_Controls.selectionProviderCheckBox2, SIGNAL(toggled(bool)), this, SLOT(SetAsSelectionProvider2(bool))); connect(m_Controls.selectionListenerCheckBox, SIGNAL(toggled(bool)), this, SLOT(SetAsSelectionListener1(bool))); connect(m_Controls.selectionListenerCheckBox2, SIGNAL(toggled(bool)), this, SLOT(SetAsSelectionListener2(bool))); connect(m_Controls.selectionProviderCheckBox3, SIGNAL(toggled(bool)), this, SLOT(SetAsSelectionProvider3(bool))); connect(m_Controls.selectionListenerCheckBox3, SIGNAL(toggled(bool)), this, SLOT(SetAsSelectionListener3(bool))); connect(m_Controls.checkOnlyVisible, SIGNAL(toggled(bool)), m_Controls.singleSlot, SLOT(SetSelectOnlyVisibleNodes(bool))); connect(m_Controls.checkOptional, SIGNAL(toggled(bool)), m_Controls.singleSlot, SLOT(SetSelectionIsOptional(bool))); connect(m_Controls.checkOnlyImages, SIGNAL(toggled(bool)), this, SLOT(OnOnlyImages(bool))); connect(m_Controls.selectionProviderCheckBox4, SIGNAL(toggled(bool)), this, SLOT(SetAsSelectionProvider4(bool))); connect(m_Controls.selectionListenerCheckBox4, SIGNAL(toggled(bool)), this, SLOT(SetAsSelectionListener4(bool))); connect(m_Controls.checkOnlyVisible_2, SIGNAL(toggled(bool)), m_Controls.multiSlot, SLOT(SetSelectOnlyVisibleNodes(bool))); connect(m_Controls.checkOptional_2, SIGNAL(toggled(bool)), m_Controls.multiSlot, SLOT(SetSelectionIsOptional(bool))); connect(m_Controls.checkOnlyImages_2, SIGNAL(toggled(bool)), this, SLOT(OnOnlyImages2(bool))); connect(m_Controls.checkOnlyUneven, SIGNAL(toggled(bool)), this, SLOT(OnOnlyUneven(bool))); } void QmitkDataStorageViewerTestView::SetAsSelectionProvider1(bool checked) { if (checked) { m_SelectionServiceConnector->SetAsSelectionProvider(GetSite()->GetSelectionProvider().Cast().GetPointer()); connect(m_ModelViewSelectionConnector.get(), SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector.get(), SLOT(ChangeServiceSelection(QList))); } else { m_SelectionServiceConnector->RemoveAsSelectionProvider(); disconnect(m_ModelViewSelectionConnector.get(), SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector.get(), SLOT(ChangeServiceSelection(QList))); } } void QmitkDataStorageViewerTestView::SetAsSelectionListener1(bool checked) { if (checked) { m_SelectionServiceConnector->AddPostSelectionListener(GetSite()->GetWorkbenchWindow()->GetSelectionService()); connect(m_SelectionServiceConnector.get(), SIGNAL(ServiceSelectionChanged(QList)), m_ModelViewSelectionConnector.get(), SLOT(SetCurrentSelection(QList))); } else { m_SelectionServiceConnector->RemovePostSelectionListener(); disconnect(m_SelectionServiceConnector.get(), SIGNAL(ServiceSelectionChanged(QList)), m_ModelViewSelectionConnector.get(), SLOT(SetCurrentSelection(QList))); } } void QmitkDataStorageViewerTestView::SetAsSelectionProvider2(bool checked) { if (checked) { m_SelectionServiceConnector2->SetAsSelectionProvider(GetSite()->GetSelectionProvider().Cast().GetPointer()); connect(m_ModelViewSelectionConnector2.get(), SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector2.get(), SLOT(ChangeServiceSelection(QList))); } else { m_SelectionServiceConnector2->RemoveAsSelectionProvider(); disconnect(m_ModelViewSelectionConnector2.get(), SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector2.get(), SLOT(ChangeServiceSelection(QList))); } } void QmitkDataStorageViewerTestView::SetAsSelectionListener2(bool checked) { if (checked) { m_SelectionServiceConnector2->AddPostSelectionListener(GetSite()->GetWorkbenchWindow()->GetSelectionService()); connect(m_SelectionServiceConnector2.get(), SIGNAL(ServiceSelectionChanged(QList)), m_ModelViewSelectionConnector2.get(), SLOT(SetCurrentSelection(QList))); } else { m_SelectionServiceConnector2->RemovePostSelectionListener(); disconnect(m_SelectionServiceConnector2.get(), SIGNAL(ServiceSelectionChanged(QList)), m_ModelViewSelectionConnector2.get(), SLOT(SetCurrentSelection(QList))); } } void QmitkDataStorageViewerTestView::SetAsSelectionProvider3(bool checked) { if (checked) { m_SelectionServiceConnector3->SetAsSelectionProvider(GetSite()->GetSelectionProvider().Cast().GetPointer()); connect(m_Controls.singleSlot, SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector3.get(), SLOT(ChangeServiceSelection(QList))); } else { m_SelectionServiceConnector3->RemoveAsSelectionProvider(); disconnect(m_Controls.singleSlot, SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector3.get(), SLOT(ChangeServiceSelection(QList))); } } void QmitkDataStorageViewerTestView::SetAsSelectionListener3(bool checked) { if (checked) { m_SelectionServiceConnector3->AddPostSelectionListener(GetSite()->GetWorkbenchWindow()->GetSelectionService()); connect(m_SelectionServiceConnector3.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.singleSlot, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); } else { m_SelectionServiceConnector3->RemovePostSelectionListener(); disconnect(m_SelectionServiceConnector3.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.singleSlot, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); } } void QmitkDataStorageViewerTestView::SetAsSelectionProvider4(bool checked) { if (checked) { m_SelectionServiceConnector4->SetAsSelectionProvider(GetSite()->GetSelectionProvider().Cast().GetPointer()); connect(m_Controls.multiSlot, SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector4.get(), SLOT(ChangeServiceSelection(QList))); } else { m_SelectionServiceConnector4->RemoveAsSelectionProvider(); disconnect(m_Controls.multiSlot, SIGNAL(CurrentSelectionChanged(QList)), m_SelectionServiceConnector4.get(), SLOT(ChangeServiceSelection(QList))); } } void QmitkDataStorageViewerTestView::SetAsSelectionListener4(bool checked) { if (checked) { m_SelectionServiceConnector4->AddPostSelectionListener(GetSite()->GetWorkbenchWindow()->GetSelectionService()); connect(m_SelectionServiceConnector4.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.multiSlot, &QmitkMultiNodeSelectionWidget::SetCurrentSelection); } else { m_SelectionServiceConnector4->RemovePostSelectionListener(); disconnect(m_SelectionServiceConnector4.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.multiSlot, &QmitkMultiNodeSelectionWidget::SetCurrentSelection); } } void QmitkDataStorageViewerTestView::OnOnlyImages(bool checked) { if (checked) { m_Controls.singleSlot->SetNodePredicate(mitk::NodePredicateDataType::New("Image")); } else { m_Controls.singleSlot->SetNodePredicate(nullptr); } }; void QmitkDataStorageViewerTestView::OnOnlyImages2(bool checked) { if (checked) { m_Controls.multiSlot->SetNodePredicate(mitk::NodePredicateDataType::New("Image")); m_Controls.multiSlot->SetInvalidInfo(QString("InvalidInfo: is displayed for invalid states. Only images allowed!")); } else { m_Controls.multiSlot->SetNodePredicate(nullptr); m_Controls.multiSlot->SetInvalidInfo(QString("InvalidInfo: is displayed for invalid states")); } }; void QmitkDataStorageViewerTestView::OnOnlyUneven(bool checked) { if (checked) { auto checkFunction = [](const QmitkMultiNodeSelectionWidget::NodeList & nodes) { if (!(nodes.size() % 2)) { std::stringstream ss; - ss << "

Invalid selection.

The number of selected nodes must be even! the current number is " << nodes.size() << ".

"; + ss << "

Invalid selection.

The number of selected nodes must be uneven! the current number is " << nodes.size() << ".

"; return ss.str(); } return std::string(); }; m_Controls.multiSlot->SetSelectionCheckFunction(checkFunction); } else { auto checkFunction = [](const QmitkMultiNodeSelectionWidget::NodeList & /*nodes*/) { return std::string(); }; m_Controls.multiSlot->SetSelectionCheckFunction(checkFunction); } }; diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/files.cmake b/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/files.cmake index cb08f4f6c7..9dd53fc4c1 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/files.cmake +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/files.cmake @@ -1,65 +1,65 @@ set(SRC_CPP_FILES QmitkFreeSurferParcellationHandler.cpp QmitkFreeSurferParcellationWidget.cpp ) set(INTERNAL_CPP_FILES QmitkConnectomicsDataView.cpp QmitkConnectomicsNetworkOperationsView.cpp QmitkConnectomicsStatisticsView.cpp QmitkNetworkHistogramCanvas.cpp QmitkRandomParcellationView.cpp Perspectives/QmitkConnectomicsPerspective.cpp mitkPluginActivator.cpp ) set(UI_FILES src/internal/QmitkConnectomicsDataViewControls.ui src/internal/QmitkConnectomicsNetworkOperationsViewControls.ui src/internal/QmitkConnectomicsStatisticsViewControls.ui src/internal/QmitkRandomParcellationViewControls.ui src/QmitkFreeSurferParcellationWidgetControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/Perspectives/QmitkConnectomicsPerspective.h src/internal/QmitkConnectomicsDataView.h src/internal/QmitkConnectomicsNetworkOperationsView.h src/internal/QmitkConnectomicsStatisticsView.h src/internal/QmitkNetworkHistogramCanvas.h src/internal/QmitkRandomParcellationView.h src/QmitkFreeSurferParcellationHandler.h src/QmitkFreeSurferParcellationWidget.h ) set(CACHED_RESOURCE_FILES plugin.xml resources/QmitkConnectomicsDataViewIcon_48.png resources/QmitkConnectomicsNetworkOperationsViewIcon_48.png resources/QmitkConnectomicsStatisticsViewIcon_48.png resources/QmitkRandomParcellationIcon.png resources/parcellation.png ) set(QRC_FILES - resources/QmitkDiffusionImaging.qrc + resources/QmitkConnectomics.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/resources/QmitkDiffusionImaging.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/resources/QmitkConnectomics.qrc similarity index 67% rename from Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/resources/QmitkDiffusionImaging.qrc rename to Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/resources/QmitkConnectomics.qrc index d0b7ede3cc..c3a3e8eb13 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/resources/QmitkDiffusionImaging.qrc +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.connectomics/resources/QmitkConnectomics.qrc @@ -1,8 +1,6 @@ - - + ConnectomicsNetwork.png QmitkRandomParcellationIcon.png - diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.denoising/resources/QmitkDiffusionImaging.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging.denoising/resources/QmitkDiffusionImaging.qrc deleted file mode 100644 index 5ab7873d2a..0000000000 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.denoising/resources/QmitkDiffusionImaging.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - denoisingicon.png - - diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/files.cmake b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/files.cmake index 1e35e55a6c..3966936404 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/files.cmake +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/files.cmake @@ -1,73 +1,77 @@ set(SRC_CPP_FILES QmitkTensorModelParametersWidget.cpp QmitkZeppelinModelParametersWidget.cpp QmitkStickModelParametersWidget.cpp QmitkDotModelParametersWidget.cpp QmitkBallModelParametersWidget.cpp QmitkAstrosticksModelParametersWidget.cpp QmitkPrototypeSignalParametersWidget.cpp ) set(INTERNAL_CPP_FILES QmitkFiberfoxView.cpp QmitkFieldmapGeneratorView.cpp QmitkFiberGenerationView.cpp mitkPluginActivator.cpp Perspectives/QmitkDIAppSyntheticDataGenerationPerspective.cpp ) set(UI_FILES src/internal/QmitkFiberfoxViewControls.ui src/internal/QmitkFieldmapGeneratorViewControls.ui src/internal/QmitkFiberGenerationViewControls.ui src/QmitkTensorModelParametersWidgetControls.ui src/QmitkZeppelinModelParametersWidgetControls.ui src/QmitkStickModelParametersWidgetControls.ui src/QmitkDotModelParametersWidgetControls.ui src/QmitkBallModelParametersWidgetControls.ui src/QmitkAstrosticksModelParametersWidgetControls.ui src/QmitkPrototypeSignalParametersWidgetControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkFiberfoxView.h src/internal/QmitkFieldmapGeneratorView.h src/internal/QmitkFiberGenerationView.h src/QmitkTensorModelParametersWidget.h src/QmitkZeppelinModelParametersWidget.h src/QmitkStickModelParametersWidget.h src/QmitkDotModelParametersWidget.h src/QmitkBallModelParametersWidget.h src/QmitkAstrosticksModelParametersWidget.h src/QmitkPrototypeSignalParametersWidget.h src/internal/Perspectives/QmitkDIAppSyntheticDataGenerationPerspective.h ) set(CACHED_RESOURCE_FILES plugin.xml resources/phantom.png resources/syntheticdata.png resources/fieldmap.png resources/models.png + resources/download.png + resources/right.png + resources/stop.png + resources/upload.png ) set(QRC_FILES - resources/QmitkDiffusionImaging.qrc + resources/QmitkFiberfox.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/QmitkDiffusionImaging.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/QmitkDiffusionImaging.qrc deleted file mode 100644 index 3bd9ee335f..0000000000 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/QmitkDiffusionImaging.qrc +++ /dev/null @@ -1,30 +0,0 @@ - - - circle.png - general_icons/download.ico - general_icons/play.ico - general_icons/plus.ico - general_icons/refresh.ico - general_icons/right.ico - general_icons/save.ico - general_icons/undo.ico - general_icons/upload.ico - general_icons/abort.ico - general_icons/copy1.ico - general_icons/copy2.ico - general_icons/cut.ico - general_icons/deny1.ico - general_icons/deny2.ico - general_icons/down.ico - general_icons/left.ico - general_icons/magn_minus.ico - general_icons/magn_plus.ico - general_icons/search.ico - general_icons/stop.ico - general_icons/up.ico - general_icons/help.ico - general_icons/pencil.ico - general_icons/edit.ico - models.png - - diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/QmitkFiberfox.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/QmitkFiberfox.qrc new file mode 100644 index 0000000000..6ecec32167 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/QmitkFiberfox.qrc @@ -0,0 +1,10 @@ + + + circle.png + refresh.xpm + download.png + right.png + stop.png + upload.png + + diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/download.png b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/download.png new file mode 100644 index 0000000000..5949729192 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/download.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/abort.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/abort.ico deleted file mode 100644 index b8edcbdc33..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/abort.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/copy1.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/copy1.ico deleted file mode 100644 index 22b77ce46a..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/copy1.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/copy2.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/copy2.ico deleted file mode 100644 index f09778af37..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/copy2.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/cut.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/cut.ico deleted file mode 100644 index bd757f5d10..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/cut.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/deny1.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/deny1.ico deleted file mode 100644 index b3d67e68e9..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/deny1.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/deny2.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/deny2.ico deleted file mode 100644 index 1c70699dc8..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/deny2.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/down.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/down.ico deleted file mode 100644 index 5a85a4136a..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/down.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/download.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/download.ico deleted file mode 100644 index a991a963c8..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/download.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/edit.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/edit.ico deleted file mode 100644 index 9f6951a514..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/edit.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/help.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/help.ico deleted file mode 100644 index 2a972dfbf8..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/help.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/left.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/left.ico deleted file mode 100644 index 964f60ad8e..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/left.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/magn_minus.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/magn_minus.ico deleted file mode 100644 index 10c7d311d0..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/magn_minus.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/magn_plus.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/magn_plus.ico deleted file mode 100644 index e520a689a0..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/magn_plus.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/pencil.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/pencil.ico deleted file mode 100644 index 0e58e1cc94..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/pencil.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/play.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/play.ico deleted file mode 100644 index d6379ce139..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/play.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/plus.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/plus.ico deleted file mode 100644 index a2b7a026b0..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/plus.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/refresh.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/refresh.ico deleted file mode 100644 index d603e55ff2..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/refresh.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/right.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/right.ico deleted file mode 100644 index 6a06a666a4..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/right.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/save.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/save.ico deleted file mode 100644 index 80c7ecdbdf..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/save.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/search.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/search.ico deleted file mode 100644 index 25802edcc9..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/search.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/stop.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/stop.ico deleted file mode 100644 index 292166968c..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/stop.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/undo.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/undo.ico deleted file mode 100644 index 2b6a67a2cb..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/undo.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/up.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/up.ico deleted file mode 100644 index e221d345e9..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/up.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/upload.ico b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/upload.ico deleted file mode 100644 index 9b85d6ed4b..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/general_icons/upload.ico and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/refresh.xpm b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/refresh.xpm new file mode 100644 index 0000000000..878265e17a --- /dev/null +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/refresh.xpm @@ -0,0 +1,322 @@ +/* XPM */ +static char * view_refresh_xpm[] = { +"32 32 287 2", +" c None", +". c #396AA9", +"+ c #3D6CA9", +"@ c #3968A8", +"# c #3565A5", +"$ c #3767A6", +"% c #3867A7", +"& c #3A6AA8", +"* c #3567A6", +"= c #3C6BA8", +"- c #4B77B0", +"; c #87A6CE", +"> c #A5BFDE", +", c #B3CAE4", +"' c #BAD0E8", +") c #B6CEE6", +"! c #AFC6E1", +"~ c #9CB8D9", +"{ c #82A3CB", +"] c #537EB5", +"^ c #3767A7", +"/ c #3869A6", +"( c #3364A4", +"_ c #3E6CA9", +": c #94B1D4", +"< c #BCD2E8", +"[ c #BBD1E8", +"} c #B9D0E7", +"| c #B8CEE6", +"1 c #B5CDE5", +"2 c #B3CBE5", +"3 c #B2CBE5", +"4 c #B4CCE5", +"5 c #AAC4E1", +"6 c #799EC9", +"7 c #3B69A9", +"8 c #3768A6", +"9 c #3F6EAA", +"0 c #AEC5E1", +"a c #BED3E9", +"b c #B8CEE7", +"c c #B6CDE6", +"d c #B1CAE4", +"e c #AFC8E3", +"f c #ADC7E3", +"g c #ABC6E3", +"h c #769AC6", +"i c #3867A6", +"j c #3868A7", +"k c #87AAD2", +"l c #A6BFDD", +"m c #C0D4E9", +"n c #BDD3E8", +"o c #B4CCE4", +"p c #A2BCDB", +"q c #9AB6D7", +"r c #9EB9DA", +"s c #ABC4E0", +"t c #BAD0E7", +"u c #B9CFE7", +"v c #AEC8E3", +"w c #AAC5E2", +"x c #A8C4E1", +"y c #A6C2E0", +"z c #A9C4E1", +"A c #B3CBE6", +"B c #9DBADB", +"C c #4574AD", +"D c #3465A4", +"E c #89AAD1", +"F c #A5C2E0", +"G c #6E94C1", +"H c #BFD4EA", +"I c #93B1D4", +"J c #4E79B2", +"K c #3969A8", +"L c #3969A7", +"M c #3868A6", +"N c #3B6AA7", +"O c #5C84B8", +"P c #8CACD2", +"Q c #B3CCE5", +"R c #9EBDDE", +"S c #8FB3D8", +"T c #81A9D3", +"U c #8CB0D7", +"V c #9FBDDD", +"W c #96B5D8", +"X c #9CBCDD", +"Y c #A2C0DF", +"Z c #A5BFDC", +"` c #5D86B8", +" . c #3B6AA8", +".. c #3768A7", +"+. c #3A6AA9", +"@. c #3464A5", +"#. c #3969A5", +"$. c #6B90C0", +"%. c #98B7D9", +"&. c #98B9DC", +"*. c #84ABD5", +"=. c #7EA7D3", +"-. c #7BA5D2", +";. c #80A9D4", +">. c #97B8DB", +",. c #79A4D1", +"'. c #A3C0DF", +"). c #3C6AA9", +"!. c #6B91C0", +"~. c #4A77B3", +"{. c #4E7BB5", +"]. c #3667A5", +"^. c #7197C5", +"/. c #A0BEDE", +"(. c #8BAFD7", +"_. c #7AA5D2", +":. c #78A3D1", +"<. c #75A1D0", +"[. c #3364A5", +"}. c #5884BB", +"|. c #5D88BE", +"1. c #517EB8", +"2. c #3666A6", +"3. c #406EAA", +"4. c #9EBBDB", +"5. c #77A2D0", +"6. c #74A0CF", +"7. c #ABC6E2", +"8. c #4172AF", +"9. c #648DC3", +"0. c #678FC5", +"a. c #4D7AB4", +"b. c #3565A3", +"c. c #4472AC", +"d. c #A2BFDF", +"e. c #87ADD6", +"f. c #76A1D0", +"g. c #739FCF", +"h. c #76A2D0", +"i. c #5783BB", +"j. c #4571AC", +"k. c #9EBCDC", +"l. c #8BB0D7", +"m. c #74A1D0", +"n. c #729FCF", +"o. c #AFC9E4", +"p. c #3F6EAB", +"q. c #A3BEDD", +"r. c #A9C4E2", +"s. c #A9C5E1", +"t. c #AFC8E4", +"u. c #B2CAE4", +"v. c #3565A4", +"w. c #3666A5", +"x. c #CADBED", +"y. c #C9DAED", +"z. c #C7D9EC", +"A. c #C2D6EA", +"B. c #C0D4EA", +"C. c #C1D5EA", +"D. c #A0BCDB", +"E. c #3769A6", +"F. c #C8DAEC", +"G. c #9BBBDD", +"H. c #3C6CA8", +"I. c #B3CBE4", +"J. c #416EAA", +"K. c #C8D9EC", +"L. c #99BADC", +"M. c #BDD2E8", +"N. c #A9C2DF", +"O. c #416EA9", +"P. c #82A9D8", +"Q. c #3869A7", +"R. c #C6D8EC", +"S. c #98B9DB", +"T. c #A7C4E1", +"U. c #B2CBE4", +"V. c #7399C5", +"W. c #3668A6", +"X. c #87ADD9", +"Y. c #C4D6E9", +"Z. c #B1CAE2", +"`. c #3F70AD", +" + c #4577B4", +".+ c #3667A6", +"++ c #C4D7EB", +"@+ c #9DBCDE", +"#+ c #B5CDE6", +"$+ c #96B8DB", +"%+ c #95B7DB", +"&+ c #95B6DA", +"*+ c #85AAD2", +"=+ c #5682B9", +"-+ c #4982C6", +";+ c #B3CDE5", +">+ c #769CCC", +",+ c #3A6CAB", +"'+ c #4476B3", +")+ c #4D81BC", +"!+ c #A9C2DE", +"~+ c #B7CEE7", +"{+ c #9FBEDE", +"]+ c #6D9BCE", +"^+ c #6A99CC", +"/+ c #719ECF", +"(+ c #82A9D4", +"_+ c #80A7D3", +":+ c #618DC1", +"<+ c #4573AF", +"[+ c #3466A5", +"}+ c #3769A9", +"|+ c #386BAD", +"1+ c #376CAE", +"2+ c #3667A9", +"3+ c #4072B0", +"4+ c #5183BE", +"5+ c #588DC7", +"6+ c #3B6EAD", +"7+ c #94B3D7", +"8+ c #5882B7", +"9+ c #A1BEDC", +"0+ c #88AED7", +"a+ c #6E9CCD", +"b+ c #6999CC", +"c+ c #6797CB", +"d+ c #6696CB", +"e+ c #729ECF", +"f+ c #76A0D0", +"g+ c #6995C8", +"h+ c #5E8DC3", +"i+ c #5A8AC2", +"j+ c #5C8CC3", +"k+ c #5E90C9", +"l+ c #5B8FC7", +"m+ c #578CC7", +"n+ c #4A7EBC", +"o+ c #3566A5", +"p+ c #9FBCDA", +"q+ c #3768A5", +"r+ c #6990C1", +"s+ c #7CA5D2", +"t+ c #6A9ACC", +"u+ c #6596CB", +"v+ c #6395CA", +"w+ c #6294CA", +"x+ c #6093C9", +"y+ c #6093CA", +"z+ c #5D91C8", +"A+ c #5B8FC8", +"B+ c #588DC6", +"C+ c #588CC7", +"D+ c #4D82BE", +"E+ c #315F98", +"F+ c #3A69A6", +"G+ c #648EC0", +"H+ c #7EA5D2", +"I+ c #7AA4D2", +"J+ c #6F9DCE", +"K+ c #6193CA", +"L+ c #5F91C8", +"M+ c #5D90C8", +"N+ c #5C90C7", +"O+ c #5C90C8", +"P+ c #588EC6", +"Q+ c #477AB7", +"R+ c #3465A5", +"S+ c #2E578E", +"T+ c #3666A2", +"U+ c #4674AD", +"V+ c #5A88BE", +"W+ c #6592C6", +"X+ c #6B98CB", +"Y+ c #6B99CC", +"Z+ c #6797CC", +"`+ c #6090C7", +" @ c #5588C1", +".@ c #4679B6", +"+@ c #32609C", +"@@ c #1B3558", +"#@ c #2B5185", +"$@ c #325E9A", +"%@ c #3464A1", +"&@ c #3463A3", +"*@ c #3464A2", +"=@ c #32609B", +"-@ c #2A4F83", +" ", +" . + @ # $ % & * ", +" = - ; > , ' ) ! ~ { ] ^ / ( ", +" _ : < [ } | 1 2 2 3 4 | 5 6 7 8 % ( ", +" 9 0 a < ' } b c 3 d e f g f 4 d h i j k ( ", +" j l m n o p q r s t u 4 v w x y z A B C D E F ( ", +" G H I J K & L L M N O P Q c g R S T U V W X Y ( ", +" = Z ` ...+.@. L #.$.%.&.*.=.-.;.>.,.'.( ", +" ).!.K ~.{.]. @ ^./.(._.:.<.<.z [. ", +" .N }.|.1.2. 8 3.4.&.5.6.<.7.# ", +" ( 8.9.0.a.b. c.d.e.f.g.h.f # ", +" ( i. j.k.l.5.m.n.h.o.# ", +" ( p.q.w r.z s.w t.u.v. ", +" ( ( ( ( ( ( ( ( ( ( ( ", +" ( w.w.w.D ( ( ( ( ( ( ", +" ( x.x.y.z.A.B.C.D.E. ( ", +" ( F.G.&.&.&.v s H. ( ", +" ( F.G.&.&.w I.J. . ( ", +" ( K.G.&.L.M.N.O.^ P. Q.D ", +" ( R.G.&.&.S.T.U.V.W. X.Y.Z.`. +.+ ", +" ( ++@+#+G.$+%+%+&+*+=+]..+ -+;+>+,+'+)+* ", +" ( A.} !+~+{+*.]+^+/+(+_+:+<+[+}+|+1+|+2+3+4+5+6+ ", +" ( C.7+( 8+9+0+a+b+c+d+b+e+5.f+g+h+i+j+k+l+m+n+o+ ", +" ( p+M q+r+l.s+t+u+v+w+x+x+x+y+z+A+B+C+D+o+ ", +" D . E+F+G+H+I+J+c+K+L+M+N+O+l+P+Q+R+ ", +" ( S+T+U+V+W+X+Y+Z+`+ @.@$ +@ ", +" @@#@$@%@&@( *@=@-@ ", +" ", +" ", +" ", +" ", +" "}; diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/right.png b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/right.png new file mode 100644 index 0000000000..3201e76d25 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/right.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/stop.png b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/stop.png new file mode 100644 index 0000000000..108c60b1a1 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/stop.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/upload.png b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/upload.png new file mode 100644 index 0000000000..6abef3f7d7 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/resources/upload.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberGenerationViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberGenerationViewControls.ui index fc21d9ed04..29f96f5379 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberGenerationViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberGenerationViewControls.ui @@ -1,2012 +1,2012 @@ QmitkFiberGenerationViewControls 0 0 413 1461 Form Save Parameters - + :/QmitkDiffusionImaging/general_icons/download.ico:/QmitkDiffusionImaging/general_icons/download.ico Load Parameters - + :/QmitkDiffusionImaging/general_icons/upload.ico:/QmitkDiffusionImaging/general_icons/upload.ico Qt::Vertical 20 40 0 0 0 381 731 Manual Fiber Design QFrame::NoFrame QFrame::Raised 0 0 0 0 false 30 30 Draw elliptical fiducial. - - :/QmitkDiffusionImaging/circle.png:/QmitkDiffusionImaging/circle.png + + :/QmitkFiberfox/circle.png:/QmitkFiberfox/circle.png 32 32 false true false 30 30 Flip fiber waypoints of selcted fiducial around one axis. - - :/QmitkDiffusionImaging/general_icons/refresh.ico:/QmitkDiffusionImaging/general_icons/refresh.ico + + :/QmitkFiberfox/refresh.xpm:/QmitkFiberfox/refresh.xpm 32 32 false true Qt::Horizontal 40 20 color: rgb(255, 0, 0); Please select an image or an existing fiber bundle to draw the fiber fiducials. If you can't provide a suitable image, generate one using the Fiberfox View. Qt::AutoText Qt::AlignJustify|Qt::AlignVCenter true QGroupBox { background-color: transparent; } Fiber Options 6 6 6 6 QFrame::NoFrame QFrame::Raised 0 0 0 0 QFrame::NoFrame QFrame::Raised 0 0 0 0 Tension: false Fiber Sampling: false 3 -1.000000000000000 1.000000000000000 0.100000000000000 0.000000000000000 3 -1.000000000000000 1.000000000000000 0.100000000000000 0.000000000000000 Bias: false Continuity: false 3 -1.000000000000000 1.000000000000000 0.100000000000000 0.000000000000000 Distance of fiber sampling points (in mm) 1 0.100000000000000 0.100000000000000 1.000000000000000 QFrame::NoFrame QFrame::Raised 0 0 0 0 6 #Fibers: false Specify number of fibers to generate for the selected bundle. 1 1000000 100 100 false QCommandLinkButton:disabled { border: none; } Generate Fibers - + :/QmitkDiffusionImaging/general_icons/right.ico:/QmitkDiffusionImaging/general_icons/right.ico QFrame::NoFrame QFrame::Raised 0 0 0 0 Select fiber distribution inside of the fiducials. Uniform Gaussian Fiber Distribution: false Variance of the gaussian 3 0.001000000000000 10.000000000000000 0.010000000000000 0.100000000000000 QFrame::NoFrame QFrame::Raised 0 0 0 0 Disable to only generate fibers if "Generate Fibers" button is pressed. Real Time Fibers true Disable to only generate fibers if "Generate Fibers" button is pressed. Advanced Options false QGroupBox { background-color: transparent; } Fiducial Options 6 6 6 6 Fiducial Attributes 6 6 6 6 Length of fiducial axis 2 2 0.000000000000000 9999.000000000000000 0.100000000000000 Axis 1: false Position X: false World Position in mm 2 -99999.000000000000000 99999.000000000000000 0.100000000000000 World Position in mm 2 -360.000000000000000 360.000000000000000 0.100000000000000 World Position in mm 2 -360.000000000000000 360.000000000000000 0.100000000000000 Length of fiducial axis 1 2 0.000000000000000 9999.000000000000000 0.100000000000000 Position Z: false Position Y: false Axis 2: false Twist: false Twist in degree 2 -180.000000000000000 180.000000000000000 0.100000000000000 All fiducials are treated as circles with the same radius as the first fiducial. Use Constant Fiducial Radius false false Align selected fiducials with voxel grid. Shifts selected fiducials to nearest voxel center. QCommandLinkButton:disabled { border: none; } Align With Grid - + :/QmitkDiffusionImaging/general_icons/right.ico:/QmitkDiffusionImaging/general_icons/right.ico 0 0 - 403 + 395 277 Random Phantom Volume Size: false Curvyness: false QFrame::NoFrame QFrame::Raised 0 0 0 0 Specify number of fibers to generate for the selected bundle. 0 90 10 30 Specify number of fibers to generate for the selected bundle. 0 90 10 15 Step Size: false QFrame::NoFrame QFrame::Raised 0 0 0 0 Specify number of fibers to generate for the selected bundle. 1 100 1 15 Specify number of fibers to generate for the selected bundle. 1 100 1 30 Streamline Density: false Specify number of fibers to generate for the selected bundle. 1 1000 1 50 Start Radii: false QFrame::NoFrame QFrame::Raised 0 0 0 0 Specify number of fibers to generate for the selected bundle. 1 90 1 5 Specify number of fibers to generate for the selected bundle. 1 90 1 45 QFrame::NoFrame QFrame::Raised 0 0 0 0 Specify number of fibers to generate for the selected bundle. 1 1000 1 250 Specify number of fibers to generate for the selected bundle. 1 1000 1 250 Specify number of fibers to generate for the selected bundle. 1 1000 1 250 Twist: false #Bundles: false QFrame::NoFrame QFrame::Raised 0 0 0 0 Specify number of fibers to generate for the selected bundle. 1 300 1 5 Specify number of fibers to generate for the selected bundle. 1 300 1 25 QFrame::NoFrame QFrame::Raised 0 0 0 0 Specify number of fibers to generate for the selected bundle. 1 10000 10 200 Specify number of fibers to generate for the selected bundle. 1 10000 10 50 true QCommandLinkButton:disabled { border: none; } Generate - + :/QmitkDiffusionImaging/general_icons/right.ico:/QmitkDiffusionImaging/general_icons/right.ico 0 0 395 283 Operations QFrame::NoFrame QFrame::Raised 0 0 0 0 Y false Rotation angle (in degree) around x-axis. 2 -360.000000000000000 360.000000000000000 0.100000000000000 Axis: false Rotation angle (in degree) around y-axis. 2 -360.000000000000000 360.000000000000000 0.100000000000000 Translation: false Translation (in mm) in direction of the z-axis. 2 -1000.000000000000000 1000.000000000000000 0.100000000000000 Translation (in mm) in direction of the y-axis. 2 -1000.000000000000000 1000.000000000000000 0.100000000000000 X false Rotation: false Z false Rotation angle (in degree) around z-axis. 2 -360.000000000000000 360.000000000000000 0.100000000000000 Translation (in mm) in direction of the x-axis. 2 -1000.000000000000000 1000.000000000000000 0.100000000000000 Scaling: false Scaling factor for selected fiber bundle along the x-axis. 0.010000000000000 10.000000000000000 0.010000000000000 1.000000000000000 Scaling factor for selected fiber bundle along the y-axis. 0.010000000000000 10.000000000000000 0.010000000000000 1.000000000000000 Scaling factor for selected fiber bundle along the z-axis. 0.010000000000000 10.000000000000000 0.010000000000000 1.000000000000000 false QCommandLinkButton:disabled { border: none; } Copy Bundles - + :/QmitkDiffusionImaging/general_icons/copy2.ico:/QmitkDiffusionImaging/general_icons/copy2.ico false QCommandLinkButton:disabled { border: none; } Transform Selection - + :/QmitkDiffusionImaging/general_icons/refresh.ico:/QmitkDiffusionImaging/general_icons/refresh.ico false QCommandLinkButton:disabled { border: none; } Join Bundles - + :/QmitkDiffusionImaging/general_icons/plus.ico:/QmitkDiffusionImaging/general_icons/plus.ico If checked, the fiducials belonging to the modified bundle are also modified. Include Fiducials true m_CircleButton m_FlipButton m_RealTimeFibers m_AdvancedOptionsBox m_DistributionBox m_VarianceBox m_FiberDensityBox m_FiberSamplingBox m_TensionBox m_ContinuityBox m_BiasBox m_GenerateFibersButton m_ConstantRadiusBox m_NumBundlesBox m_MinDensityBox m_MaxDensityBox m_MinTwistBox m_MaxTwistBox m_CurvyMinBox m_CurvyMaxBox m_SizeMinBox m_SizeMaxBox m_StepSizeMinBox m_StepSizeMaxBox m_VolumeSizeX m_VolumeSizeY m_VolumeSizeZ m_RandomPhantomButton m_XrotBox m_YrotBox m_ZrotBox m_XtransBox m_YtransBox m_ZtransBox m_XscaleBox m_YscaleBox m_ZscaleBox m_TransformBundlesButton m_CopyBundlesButton m_JoinBundlesButton m_IncludeFiducials m_SaveParametersButton m_LoadParametersButton m_FidAxis1 m_FidPosY m_FidPosZ m_FidAxis2 m_FidTwist m_AlignOnGrid m_FidPosX - + diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxView.cpp index 746806fec4..be3ad8ad0a 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxView.cpp @@ -1,2156 +1,2181 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkFiberfoxView.h" // MITK #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RAPIDXML_NO_EXCEPTIONS #include #include #include #include #include "usModuleRegistry.h" #include #include #include #include #include #include #include #include #include #include "mitkNodePredicateDataType.h" #include #include #include #include QmitkFiberfoxWorker::QmitkFiberfoxWorker(QmitkFiberfoxView* view) : m_View(view) { } void QmitkFiberfoxWorker::run() { try{ m_View->m_TractsToDwiFilter->Update(); } catch( ... ) { } m_View->m_Thread.quit(); } const std::string QmitkFiberfoxView::VIEW_ID = "org.mitk.views.fiberfoxview"; QmitkFiberfoxView::QmitkFiberfoxView() : QmitkAbstractView() , m_Controls( 0 ) , m_SelectedImageNode( nullptr ) , m_Worker(this) , m_ThreadIsRunning(false) { m_Worker.moveToThread(&m_Thread); connect(&m_Thread, SIGNAL(started()), this, SLOT(BeforeThread())); connect(&m_Thread, SIGNAL(started()), &m_Worker, SLOT(run())); connect(&m_Thread, SIGNAL(finished()), this, SLOT(AfterThread())); // connect(&m_Thread, SIGNAL(terminated()), this, SLOT(AfterThread())); m_SimulationTimer = new QTimer(this); } void QmitkFiberfoxView::KillThread() { MITK_INFO << "Aborting DWI simulation."; m_TractsToDwiFilter->SetAbortGenerateData(true); m_Controls->m_AbortSimulationButton->setEnabled(false); m_Controls->m_AbortSimulationButton->setText("Aborting simulation ..."); } void QmitkFiberfoxView::BeforeThread() { m_SimulationTime = QTime::currentTime(); m_SimulationTimer->start(100); m_Controls->m_AbortSimulationButton->setVisible(true); m_Controls->m_GenerateImageButton->setVisible(false); m_Controls->m_SimulationStatusText->setVisible(true); m_ThreadIsRunning = true; } void QmitkFiberfoxView::AfterThread() { UpdateSimulationStatus(); m_SimulationTimer->stop(); m_Controls->m_AbortSimulationButton->setVisible(false); m_Controls->m_AbortSimulationButton->setEnabled(true); m_Controls->m_AbortSimulationButton->setText("Abort simulation"); m_Controls->m_GenerateImageButton->setVisible(true); m_ThreadIsRunning = false; QString statusText; FiberfoxParameters parameters; mitk::Image::Pointer mitkImage = mitk::Image::New(); statusText = QString(m_TractsToDwiFilter->GetStatusText().c_str()); if (m_TractsToDwiFilter->GetAbortGenerateData()) { MITK_INFO << "Simulation aborted."; return; } parameters = m_TractsToDwiFilter->GetParameters(); mitkImage = mitk::GrabItkImageMemory( m_TractsToDwiFilter->GetOutput() ); - mitk::DiffusionPropertyHelper::SetGradientContainer(mitkImage, parameters.m_SignalGen.GetItkGradientContainer()); - mitk::DiffusionPropertyHelper::SetReferenceBValue(mitkImage, parameters.m_SignalGen.GetBvalue()); - mitk::DiffusionPropertyHelper::InitializeImage( mitkImage ); + if (parameters.m_SignalGen.GetNumWeightedVolumes() > 0) + { + mitk::DiffusionPropertyHelper::SetGradientContainer(mitkImage, parameters.m_SignalGen.GetItkGradientContainer()); + mitk::DiffusionPropertyHelper::SetReferenceBValue(mitkImage, parameters.m_SignalGen.GetBvalue()); + mitk::DiffusionPropertyHelper::InitializeImage( mitkImage ); + } parameters.m_Misc.m_ResultNode->SetData( mitkImage ); GetDataStorage()->Add(parameters.m_Misc.m_ResultNode, parameters.m_Misc.m_ParentNode); if (m_Controls->m_VolumeFractionsBox->isChecked()) { + if (m_TractsToDwiFilter->GetTickImage().IsNotNull()) + { + mitk::Image::Pointer mitkImage = mitk::Image::New(); + itk::TractsToDWIImageFilter< short >::Float2DImageType::Pointer itkImage = m_TractsToDwiFilter->GetTickImage(); + mitkImage = mitk::GrabItkImageMemory( itkImage.GetPointer() ); + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData( mitkImage ); + node->SetName("Tick Image"); + GetDataStorage()->Add(node, parameters.m_Misc.m_ResultNode); + } + + if (m_TractsToDwiFilter->GetRfImage().IsNotNull()) + { + mitk::Image::Pointer mitkImage = mitk::Image::New(); + itk::TractsToDWIImageFilter< short >::Float2DImageType::Pointer itkImage = m_TractsToDwiFilter->GetRfImage(); + mitkImage = mitk::GrabItkImageMemory( itkImage.GetPointer() ); + mitk::DataNode::Pointer node = mitk::DataNode::New(); + node->SetData( mitkImage ); + node->SetName("RF Image"); + GetDataStorage()->Add(node, parameters.m_Misc.m_ResultNode); + } + if (m_TractsToDwiFilter->GetPhaseImage().IsNotNull()) { mitk::Image::Pointer phaseImage = mitk::Image::New(); itk::TractsToDWIImageFilter< short >::DoubleDwiType::Pointer itkPhase = m_TractsToDwiFilter->GetPhaseImage(); phaseImage = mitk::GrabItkImageMemory( itkPhase.GetPointer() ); mitk::DataNode::Pointer phaseNode = mitk::DataNode::New(); phaseNode->SetData( phaseImage ); phaseNode->SetName("Phase Image"); GetDataStorage()->Add(phaseNode, parameters.m_Misc.m_ResultNode); } if (m_TractsToDwiFilter->GetKspaceImage().IsNotNull()) { mitk::Image::Pointer image = mitk::Image::New(); itk::TractsToDWIImageFilter< short >::DoubleDwiType::Pointer itkImage = m_TractsToDwiFilter->GetKspaceImage(); image = mitk::GrabItkImageMemory( itkImage.GetPointer() ); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("k-Space"); GetDataStorage()->Add(node, parameters.m_Misc.m_ResultNode); } { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(m_TractsToDwiFilter->GetCoilPointset()); node->SetName("Coil Positions"); node->SetProperty("pointsize", mitk::FloatProperty::New(parameters.m_SignalGen.m_ImageSpacing[0]/4)); node->SetProperty("color", mitk::ColorProperty::New(0, 1, 0)); GetDataStorage()->Add(node, parameters.m_Misc.m_ResultNode); } int c = 1; std::vector< itk::TractsToDWIImageFilter< short >::DoubleDwiType::Pointer > output_real = m_TractsToDwiFilter->GetOutputImagesReal(); for (auto real : output_real) { mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(real.GetPointer()); image->SetVolume(real->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("Coil-"+QString::number(c).toStdString()+"-real"); GetDataStorage()->Add(node, parameters.m_Misc.m_ResultNode); ++c; } c = 1; std::vector< itk::TractsToDWIImageFilter< short >::DoubleDwiType::Pointer > output_imag = m_TractsToDwiFilter->GetOutputImagesImag(); for (auto imag : output_imag) { mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(imag.GetPointer()); image->SetVolume(imag->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("Coil-"+QString::number(c).toStdString()+"-imag"); GetDataStorage()->Add(node, parameters.m_Misc.m_ResultNode); ++c; } std::vector< itk::TractsToDWIImageFilter< short >::ItkDoubleImgType::Pointer > volumeFractions = m_TractsToDwiFilter->GetVolumeFractions(); for (unsigned int k=0; kInitializeByItk(volumeFractions.at(k).GetPointer()); image->SetVolume(volumeFractions.at(k)->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("CompartmentVolume-"+QString::number(k).toStdString()); GetDataStorage()->Add(node, parameters.m_Misc.m_ResultNode); } } m_TractsToDwiFilter = nullptr; if (parameters.m_Misc.m_AfterSimulationMessage.size()>0) QMessageBox::information( nullptr, "Warning", parameters.m_Misc.m_AfterSimulationMessage.c_str()); mitk::BaseData::Pointer basedata = parameters.m_Misc.m_ResultNode->GetData(); if (basedata.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( basedata->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } if (!parameters.m_Misc.m_OutputPath.empty()) { try{ QString outputFileName(parameters.m_Misc.m_OutputPath.c_str()); outputFileName += parameters.m_Misc.m_ResultNode->GetName().c_str(); outputFileName.replace(QString("."), QString("_")); SaveParameters(outputFileName+".ffp"); outputFileName += ".dwi"; QString status("Saving output image to "); status += outputFileName; m_Controls->m_SimulationStatusText->append(status); mitk::IOUtil::Save(mitkImage, outputFileName.toStdString()); m_Controls->m_SimulationStatusText->append("File saved successfully."); } catch (itk::ExceptionObject &e) { QString status("Exception during DWI writing: "); status += e.GetDescription(); m_Controls->m_SimulationStatusText->append(status); } catch (...) { m_Controls->m_SimulationStatusText->append("Unknown exception during DWI writing!"); } } parameters.m_SignalGen.m_FrequencyMap = nullptr; } void QmitkFiberfoxView::UpdateSimulationStatus() { QString statusText = QString(m_TractsToDwiFilter->GetStatusText().c_str()); if (QString::compare(m_SimulationStatusText,statusText)!=0) { m_Controls->m_SimulationStatusText->clear(); m_Controls->m_SimulationStatusText->setText(statusText); QScrollBar *vScrollBar = m_Controls->m_SimulationStatusText->verticalScrollBar(); vScrollBar->triggerAction(QScrollBar::SliderToMaximum); } } // Destructor QmitkFiberfoxView::~QmitkFiberfoxView() { delete m_SimulationTimer; } void QmitkFiberfoxView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkFiberfoxViewControls; m_Controls->setupUi( parent ); m_Controls->m_StickWidget1->setVisible(true); m_Controls->m_StickWidget2->setVisible(false); m_Controls->m_ZeppelinWidget1->setVisible(false); m_Controls->m_ZeppelinWidget2->setVisible(false); m_Controls->m_TensorWidget1->setVisible(false); m_Controls->m_TensorWidget2->setVisible(false); m_Controls->m_BallWidget1->setVisible(true); m_Controls->m_BallWidget2->setVisible(false); m_Controls->m_BallWidget2->SetT1(4658); m_Controls->m_BallWidget2->SetT2(2200); m_Controls->m_AstrosticksWidget1->setVisible(false); m_Controls->m_AstrosticksWidget2->setVisible(false); m_Controls->m_AstrosticksWidget2->SetT1(4658); m_Controls->m_AstrosticksWidget2->SetT2(2200); m_Controls->m_DotWidget1->setVisible(false); m_Controls->m_DotWidget2->setVisible(false); m_Controls->m_DotWidget2->SetT1(4658); m_Controls->m_DotWidget2->SetT2(2200); m_Controls->m_PrototypeWidget1->setVisible(false); m_Controls->m_PrototypeWidget2->setVisible(false); m_Controls->m_PrototypeWidget3->setVisible(false); m_Controls->m_PrototypeWidget4->setVisible(false); m_Controls->m_PrototypeWidget3->SetMinFa(0.0); m_Controls->m_PrototypeWidget3->SetMaxFa(0.15); m_Controls->m_PrototypeWidget4->SetMinFa(0.0); m_Controls->m_PrototypeWidget4->SetMaxFa(0.15); m_Controls->m_PrototypeWidget3->SetMinAdc(0.0); m_Controls->m_PrototypeWidget3->SetMaxAdc(0.001); m_Controls->m_PrototypeWidget4->SetMinAdc(0.003); m_Controls->m_PrototypeWidget4->SetMaxAdc(0.004); m_Controls->m_Comp2FractionFrame->setVisible(false); m_Controls->m_Comp4FractionFrame->setVisible(false); m_Controls->m_DiffusionPropsMessage->setVisible(false); m_Controls->m_GeometryMessage->setVisible(false); m_Controls->m_AdvancedSignalOptionsFrame->setVisible(false); m_Controls->m_NoiseFrame->setVisible(false); m_Controls->m_ZeroRinging->setVisible(false); m_Controls->m_GhostFrame->setVisible(false); m_Controls->m_DistortionsFrame->setVisible(false); m_Controls->m_EddyFrame->setVisible(false); m_Controls->m_SpikeFrame->setVisible(false); m_Controls->m_AliasingFrame->setVisible(false); m_Controls->m_MotionArtifactFrame->setVisible(false); m_Controls->m_DriftFrame->setVisible(false); m_ParameterFile = QDir::currentPath()+"/param.ffp"; m_Controls->m_AbortSimulationButton->setVisible(false); m_Controls->m_SimulationStatusText->setVisible(false); m_Controls->m_FrequencyMapBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_Comp1VolumeFraction->SetDataStorage(this->GetDataStorage()); m_Controls->m_Comp2VolumeFraction->SetDataStorage(this->GetDataStorage()); m_Controls->m_Comp3VolumeFraction->SetDataStorage(this->GetDataStorage()); m_Controls->m_Comp4VolumeFraction->SetDataStorage(this->GetDataStorage()); m_Controls->m_MaskComboBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_TemplateComboBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_FiberBundleComboBox->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType::Pointer isFiberBundle = mitk::TNodePredicateDataType::New(); mitk::TNodePredicateDataType::Pointer isMitkImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateIsDWI::Pointer isDwi = mitk::NodePredicateIsDWI::New( ); mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdf = mitk::NodePredicateDataType::New("Odfmage"); mitk::NodePredicateOr::Pointer isDiffusionImage = mitk::NodePredicateOr::New(isDwi, isDti); isDiffusionImage = mitk::NodePredicateOr::New(isDiffusionImage, isOdf); mitk::NodePredicateNot::Pointer noDiffusionImage = mitk::NodePredicateNot::New(isDiffusionImage); mitk::NodePredicateAnd::Pointer isNonDiffMitkImage = mitk::NodePredicateAnd::New(isMitkImage, noDiffusionImage); mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isBinaryMitkImage = mitk::NodePredicateAnd::New( isNonDiffMitkImage, isBinaryPredicate ); m_Controls->m_FrequencyMapBox->SetPredicate(isNonDiffMitkImage); m_Controls->m_Comp1VolumeFraction->SetPredicate(isNonDiffMitkImage); m_Controls->m_Comp1VolumeFraction->SetZeroEntryText("--"); m_Controls->m_Comp2VolumeFraction->SetPredicate(isNonDiffMitkImage); m_Controls->m_Comp2VolumeFraction->SetZeroEntryText("--"); m_Controls->m_Comp3VolumeFraction->SetPredicate(isNonDiffMitkImage); m_Controls->m_Comp3VolumeFraction->SetZeroEntryText("--"); m_Controls->m_Comp4VolumeFraction->SetPredicate(isNonDiffMitkImage); m_Controls->m_Comp4VolumeFraction->SetZeroEntryText("--"); m_Controls->m_MaskComboBox->SetPredicate(isBinaryMitkImage); m_Controls->m_MaskComboBox->SetZeroEntryText("--"); m_Controls->m_TemplateComboBox->SetPredicate(isMitkImage); m_Controls->m_TemplateComboBox->SetZeroEntryText("--"); m_Controls->m_FiberBundleComboBox->SetPredicate(isFiberBundle); m_Controls->m_FiberBundleComboBox->SetZeroEntryText("--"); QFont font; font.setFamily("Courier"); font.setStyleHint(QFont::Monospace); font.setFixedPitch(true); font.setPointSize(7); m_Controls->m_SimulationStatusText->setFont(font); connect( m_SimulationTimer, SIGNAL(timeout()), this, SLOT(UpdateSimulationStatus()) ); connect((QObject*) m_Controls->m_AbortSimulationButton, SIGNAL(clicked()), (QObject*) this, SLOT(KillThread())); connect((QObject*) m_Controls->m_GenerateImageButton, SIGNAL(clicked()), (QObject*) this, SLOT(GenerateImage())); connect((QObject*) m_Controls->m_AddNoise, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddNoise(int))); connect((QObject*) m_Controls->m_AddGhosts, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddGhosts(int))); connect((QObject*) m_Controls->m_AddDistortions, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddDistortions(int))); connect((QObject*) m_Controls->m_AddEddy, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddEddy(int))); connect((QObject*) m_Controls->m_AddSpikes, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddSpikes(int))); connect((QObject*) m_Controls->m_AddAliasing, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddAliasing(int))); connect((QObject*) m_Controls->m_AddMotion, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddMotion(int))); connect((QObject*) m_Controls->m_AddDrift, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddDrift(int))); connect((QObject*) m_Controls->m_AddGibbsRinging, SIGNAL(stateChanged(int)), (QObject*) this, SLOT(OnAddRinging(int))); connect((QObject*) m_Controls->m_Compartment1Box, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(Comp1ModelFrameVisibility(int))); connect((QObject*) m_Controls->m_Compartment2Box, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(Comp2ModelFrameVisibility(int))); connect((QObject*) m_Controls->m_Compartment3Box, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(Comp3ModelFrameVisibility(int))); connect((QObject*) m_Controls->m_Compartment4Box, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(Comp4ModelFrameVisibility(int))); connect((QObject*) m_Controls->m_AdvancedOptionsBox_2, SIGNAL( stateChanged(int)), (QObject*) this, SLOT(ShowAdvancedOptions(int))); connect((QObject*) m_Controls->m_UseBvalsBvecsBox, SIGNAL( stateChanged(int)), (QObject*) this, SLOT(OnBvalsBvecsCheck(int))); connect((QObject*) m_Controls->m_SaveParametersButton, SIGNAL(clicked()), (QObject*) this, SLOT(SaveParameters())); connect((QObject*) m_Controls->m_LoadParametersButton, SIGNAL(clicked()), (QObject*) this, SLOT(LoadParameters())); connect((QObject*) m_Controls->m_OutputPathButton, SIGNAL(clicked()), (QObject*) this, SLOT(SetOutputPath())); connect((QObject*) m_Controls->m_LoadBvalsButton, SIGNAL(clicked()), (QObject*) this, SLOT(SetBvalsEdit())); connect((QObject*) m_Controls->m_LoadBvecsButton, SIGNAL(clicked()), (QObject*) this, SLOT(SetBvecsEdit())); connect((QObject*) m_Controls->m_MaskComboBox, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(OnMaskSelected(int))); connect((QObject*) m_Controls->m_TemplateComboBox, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(OnTemplateSelected(int))); connect((QObject*) m_Controls->m_FiberBundleComboBox, SIGNAL(currentIndexChanged(int)), (QObject*) this, SLOT(OnFibSelected(int))); connect((QObject*) m_Controls->m_LineReadoutTimeBox, SIGNAL( valueChanged(double)), (QObject*) this, SLOT(OnTlineChanged())); connect((QObject*) m_Controls->m_SizeX, SIGNAL( valueChanged(int)), (QObject*) this, SLOT(OnTlineChanged())); } OnTlineChanged(); UpdateGui(); } void QmitkFiberfoxView::OnTlineChanged() { double num_pix_line = 0; if (m_Controls->m_TemplateComboBox->GetSelectedNode().IsNotNull()) // use geometry of selected image { mitk::Image::Pointer img = dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()); num_pix_line = img->GetDimension(0); } else if (m_Controls->m_MaskComboBox->GetSelectedNode().IsNotNull()) // use geometry of mask image { mitk::Image::Pointer img = dynamic_cast(m_Controls->m_MaskComboBox->GetSelectedNode()->GetData()); num_pix_line = img->GetDimension(0); } else { num_pix_line = m_Controls->m_SizeX->value(); } double value = static_cast(m_Controls->m_LineReadoutTimeBox->value())/1000.0; // line readout time in seconds double dweel_pp = value/num_pix_line; // pixel readout time in seconds double bw = 1/dweel_pp; double bw_pp = bw/num_pix_line; std::string tt = "Bandwidth:\n" + boost::lexical_cast(itk::Math::Round(bw)) + "Hz\n" + boost::lexical_cast(itk::Math::Round(bw_pp)) + "Hz/Px"; m_Controls->m_LineReadoutTimeBox->setToolTip(tt.c_str()); } void QmitkFiberfoxView::OnMaskSelected(int ) { UpdateGui(); } void QmitkFiberfoxView::OnTemplateSelected(int ) { UpdateGui(); } void QmitkFiberfoxView::OnFibSelected(int ) { UpdateGui(); } void QmitkFiberfoxView::OnBvalsBvecsCheck(int ) { UpdateGui(); } void QmitkFiberfoxView::UpdateParametersFromGui() { m_Parameters.ClearSignalParameters(); m_Parameters.m_Misc.m_CheckAdvancedSignalOptionsBox = m_Controls->m_AdvancedOptionsBox_2->isChecked(); - m_Parameters.m_Misc.m_CheckOutputVolumeFractionsBox = m_Controls->m_VolumeFractionsBox->isChecked(); + m_Parameters.m_Misc.m_OutputAdditionalImages = m_Controls->m_VolumeFractionsBox->isChecked(); std::string outputPath = m_Controls->m_SavePathEdit->text().toStdString(); if (outputPath.compare("-")!=0) { m_Parameters.m_Misc.m_OutputPath = outputPath; m_Parameters.m_Misc.m_OutputPath += "/"; } else { m_Parameters.m_Misc.m_OutputPath = ""; } if (m_Controls->m_MaskComboBox->GetSelectedNode().IsNotNull()) { mitk::Image::Pointer mitkMaskImage = dynamic_cast(m_Controls->m_MaskComboBox->GetSelectedNode()->GetData()); mitk::CastToItkImage(mitkMaskImage, m_Parameters.m_SignalGen.m_MaskImage); itk::ImageDuplicator::Pointer duplicator = itk::ImageDuplicator::New(); duplicator->SetInputImage(m_Parameters.m_SignalGen.m_MaskImage); duplicator->Update(); m_Parameters.m_SignalGen.m_MaskImage = duplicator->GetOutput(); } if (m_Controls->m_TemplateComboBox->GetSelectedNode().IsNotNull() && mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( m_Controls->m_TemplateComboBox->GetSelectedNode())) // use parameters of selected DWI { mitk::Image::Pointer dwi = dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(dwi, itkVectorImagePointer); m_Parameters.m_SignalGen.m_ImageRegion = itkVectorImagePointer->GetLargestPossibleRegion(); m_Parameters.m_SignalGen.m_ImageSpacing = itkVectorImagePointer->GetSpacing(); m_Parameters.m_SignalGen.m_ImageOrigin = itkVectorImagePointer->GetOrigin(); m_Parameters.m_SignalGen.m_ImageDirection = itkVectorImagePointer->GetDirection(); m_Parameters.SetBvalue(mitk::DiffusionPropertyHelper::GetReferenceBValue(dwi)); m_Parameters.SetGradienDirections(mitk::DiffusionPropertyHelper::GetOriginalGradientContainer(dwi)); } else if (m_Controls->m_TemplateComboBox->GetSelectedNode().IsNotNull()) // use geometry of selected image { mitk::Image::Pointer img = dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()); itk::Image< float, 3 >::Pointer itkImg = itk::Image< float, 3 >::New(); CastToItkImage< itk::Image< float, 3 > >(img, itkImg); m_Parameters.m_SignalGen.m_ImageRegion = itkImg->GetLargestPossibleRegion(); m_Parameters.m_SignalGen.m_ImageSpacing = itkImg->GetSpacing(); m_Parameters.m_SignalGen.m_ImageOrigin = itkImg->GetOrigin(); m_Parameters.m_SignalGen.m_ImageDirection = itkImg->GetDirection(); if (m_Controls->m_UseBvalsBvecsBox->isChecked()) { double bval; m_Parameters.SetGradienDirections( mitk::gradients::ReadBvalsBvecs(m_Controls->m_LoadBvalsEdit->text().toStdString(), m_Controls->m_LoadBvecsEdit->text().toStdString(), bval) ); m_Parameters.SetBvalue(bval); } else { m_Parameters.SetNumWeightedVolumes(m_Controls->m_NumGradientsBox->value()); m_Parameters.SetBvalue(m_Controls->m_BvalueBox->value()); m_Parameters.GenerateGradientHalfShell(); } } else if (m_Parameters.m_SignalGen.m_MaskImage.IsNotNull()) // use geometry of mask image { ItkUcharImgType::Pointer itkImg = m_Parameters.m_SignalGen.m_MaskImage; m_Parameters.m_SignalGen.m_ImageRegion = itkImg->GetLargestPossibleRegion(); m_Parameters.m_SignalGen.m_ImageSpacing = itkImg->GetSpacing(); m_Parameters.m_SignalGen.m_ImageOrigin = itkImg->GetOrigin(); m_Parameters.m_SignalGen.m_ImageDirection = itkImg->GetDirection(); if (m_Controls->m_UseBvalsBvecsBox->isChecked()) { double bval; m_Parameters.SetGradienDirections( mitk::gradients::ReadBvalsBvecs(m_Controls->m_LoadBvalsEdit->text().toStdString(), m_Controls->m_LoadBvecsEdit->text().toStdString(), bval) ); m_Parameters.SetBvalue(bval); } else { m_Parameters.SetNumWeightedVolumes(m_Controls->m_NumGradientsBox->value()); m_Parameters.SetBvalue(m_Controls->m_BvalueBox->value()); m_Parameters.GenerateGradientHalfShell(); } } else // use GUI parameters { m_Parameters.m_SignalGen.m_ImageRegion.SetSize(0, m_Controls->m_SizeX->value()); m_Parameters.m_SignalGen.m_ImageRegion.SetSize(1, m_Controls->m_SizeY->value()); m_Parameters.m_SignalGen.m_ImageRegion.SetSize(2, m_Controls->m_SizeZ->value()); m_Parameters.m_SignalGen.m_ImageSpacing[0] = m_Controls->m_SpacingX->value(); m_Parameters.m_SignalGen.m_ImageSpacing[1] = m_Controls->m_SpacingY->value(); m_Parameters.m_SignalGen.m_ImageSpacing[2] = m_Controls->m_SpacingZ->value(); m_Parameters.m_SignalGen.m_ImageOrigin[0] = m_Parameters.m_SignalGen.m_ImageSpacing[0]/2; m_Parameters.m_SignalGen.m_ImageOrigin[1] = m_Parameters.m_SignalGen.m_ImageSpacing[1]/2; m_Parameters.m_SignalGen.m_ImageOrigin[2] = m_Parameters.m_SignalGen.m_ImageSpacing[2]/2; m_Parameters.m_SignalGen.m_ImageDirection.SetIdentity(); if (m_Controls->m_UseBvalsBvecsBox->isChecked()) { double bval; m_Parameters.SetGradienDirections( mitk::gradients::ReadBvalsBvecs(m_Controls->m_LoadBvalsEdit->text().toStdString(), m_Controls->m_LoadBvecsEdit->text().toStdString(), bval) ); m_Parameters.SetBvalue(bval); } else { m_Parameters.SetNumWeightedVolumes(m_Controls->m_NumGradientsBox->value()); m_Parameters.SetBvalue(m_Controls->m_BvalueBox->value()); m_Parameters.GenerateGradientHalfShell(); } } // signal relaxation m_Parameters.m_SignalGen.m_DoSimulateRelaxation = false; if (m_Controls->m_RelaxationBox->isChecked()) { m_Parameters.m_SignalGen.m_DoSimulateRelaxation = true; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Relaxation", BoolProperty::New(true)); m_Parameters.m_Misc.m_ArtifactModelString += "_RELAX"; } m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition = m_Parameters.m_SignalGen.m_DoSimulateRelaxation; // N/2 ghosts m_Parameters.m_Misc.m_DoAddGhosts = m_Controls->m_AddGhosts->isChecked(); m_Parameters.m_SignalGen.m_KspaceLineOffset = m_Controls->m_kOffsetBox->value(); if (m_Controls->m_AddGhosts->isChecked()) { m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; m_Parameters.m_Misc.m_ArtifactModelString += "_GHOST"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Ghost", DoubleProperty::New(m_Parameters.m_SignalGen.m_KspaceLineOffset)); } // Aliasing m_Parameters.m_Misc.m_DoAddAliasing = m_Controls->m_AddAliasing->isChecked(); m_Parameters.m_SignalGen.m_CroppingFactor = (100-m_Controls->m_WrapBox->value())/100; if (m_Controls->m_AddAliasing->isChecked()) { m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; m_Parameters.m_Misc.m_ArtifactModelString += "_ALIASING"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Aliasing", DoubleProperty::New(m_Controls->m_WrapBox->value())); } // Spikes m_Parameters.m_Misc.m_DoAddSpikes = m_Controls->m_AddSpikes->isChecked(); m_Parameters.m_SignalGen.m_Spikes = m_Controls->m_SpikeNumBox->value(); m_Parameters.m_SignalGen.m_SpikeAmplitude = m_Controls->m_SpikeScaleBox->value(); if (m_Controls->m_AddSpikes->isChecked()) { m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; m_Parameters.m_Misc.m_ArtifactModelString += "_SPIKES"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Spikes.Number", IntProperty::New(m_Parameters.m_SignalGen.m_Spikes)); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Spikes.Amplitude", DoubleProperty::New(m_Parameters.m_SignalGen.m_SpikeAmplitude)); } // Drift m_Parameters.m_SignalGen.m_DoAddDrift = m_Controls->m_AddDrift->isChecked(); m_Parameters.m_SignalGen.m_Drift = static_cast(m_Controls->m_DriftFactor->value())/100; if (m_Controls->m_AddDrift->isChecked()) { m_Parameters.m_Misc.m_ArtifactModelString += "_DRIFT"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Drift", FloatProperty::New(m_Parameters.m_SignalGen.m_Drift)); } // gibbs ringing m_Parameters.m_SignalGen.m_DoAddGibbsRinging = m_Controls->m_AddGibbsRinging->isChecked(); m_Parameters.m_SignalGen.m_ZeroRinging = m_Controls->m_ZeroRinging->value(); if (m_Controls->m_AddGibbsRinging->isChecked()) { m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Ringing", BoolProperty::New(true)); m_Parameters.m_Misc.m_ArtifactModelString += "_RINGING"; } // add distortions m_Parameters.m_Misc.m_DoAddDistortions = m_Controls->m_AddDistortions->isChecked(); if (m_Controls->m_AddDistortions->isChecked() && m_Controls->m_FrequencyMapBox->GetSelectedNode().IsNotNull()) { mitk::DataNode::Pointer fMapNode = m_Controls->m_FrequencyMapBox->GetSelectedNode(); mitk::Image* img = dynamic_cast(fMapNode->GetData()); ItkFloatImgType::Pointer itkImg = ItkFloatImgType::New(); CastToItkImage< ItkFloatImgType >(img, itkImg); if (m_Controls->m_TemplateComboBox->GetSelectedNode().IsNull()) // use geometry of frequency map { m_Parameters.m_SignalGen.m_ImageRegion = itkImg->GetLargestPossibleRegion(); m_Parameters.m_SignalGen.m_ImageSpacing = itkImg->GetSpacing(); m_Parameters.m_SignalGen.m_ImageOrigin = itkImg->GetOrigin(); m_Parameters.m_SignalGen.m_ImageDirection = itkImg->GetDirection(); } m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; itk::ImageDuplicator::Pointer duplicator = itk::ImageDuplicator::New(); duplicator->SetInputImage(itkImg); duplicator->Update(); m_Parameters.m_SignalGen.m_FrequencyMap = duplicator->GetOutput(); m_Parameters.m_Misc.m_ArtifactModelString += "_DISTORTED"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Distortions", BoolProperty::New(true)); } m_Parameters.m_SignalGen.m_EddyStrength = m_Controls->m_EddyGradientStrength->value(); m_Parameters.m_Misc.m_DoAddEddyCurrents = m_Controls->m_AddEddy->isChecked(); if (m_Controls->m_AddEddy->isChecked()) { m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; m_Parameters.m_Misc.m_ArtifactModelString += "_EDDY"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Eddy-strength", DoubleProperty::New(m_Parameters.m_SignalGen.m_EddyStrength)); } // Motion m_Parameters.m_SignalGen.m_DoAddMotion = false; m_Parameters.m_SignalGen.m_DoRandomizeMotion = m_Controls->m_RandomMotion->isChecked(); m_Parameters.m_SignalGen.m_Translation[0] = m_Controls->m_MaxTranslationBoxX->value(); m_Parameters.m_SignalGen.m_Translation[1] = m_Controls->m_MaxTranslationBoxY->value(); m_Parameters.m_SignalGen.m_Translation[2] = m_Controls->m_MaxTranslationBoxZ->value(); m_Parameters.m_SignalGen.m_Rotation[0] = m_Controls->m_MaxRotationBoxX->value(); m_Parameters.m_SignalGen.m_Rotation[1] = m_Controls->m_MaxRotationBoxY->value(); m_Parameters.m_SignalGen.m_Rotation[2] = m_Controls->m_MaxRotationBoxZ->value(); m_Parameters.m_SignalGen.m_MotionVolumes.clear(); m_Parameters.m_Misc.m_MotionVolumesBox = m_Controls->m_MotionVolumesBox->text().toStdString(); if ( m_Controls->m_AddMotion->isChecked()) { m_Parameters.m_SignalGen.m_DoAddMotion = true; m_Parameters.m_Misc.m_ArtifactModelString += "_MOTION"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Random", BoolProperty::New(m_Parameters.m_SignalGen.m_DoRandomizeMotion)); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Translation-x", DoubleProperty::New(m_Parameters.m_SignalGen.m_Translation[0])); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Translation-y", DoubleProperty::New(m_Parameters.m_SignalGen.m_Translation[1])); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Translation-z", DoubleProperty::New(m_Parameters.m_SignalGen.m_Translation[2])); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Rotation-x", DoubleProperty::New(m_Parameters.m_SignalGen.m_Rotation[0])); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Rotation-y", DoubleProperty::New(m_Parameters.m_SignalGen.m_Rotation[1])); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Motion.Rotation-z", DoubleProperty::New(m_Parameters.m_SignalGen.m_Rotation[2])); if ( m_Parameters.m_Misc.m_MotionVolumesBox == "random" ) { for ( size_t i=0; i < m_Parameters.m_SignalGen.GetNumVolumes(); ++i ) { m_Parameters.m_SignalGen.m_MotionVolumes.push_back( bool( rand()%2 ) ); } MITK_DEBUG << "QmitkFiberfoxView.cpp: Case m_Misc.m_MotionVolumesBox == \"random\"."; } else if ( ! m_Parameters.m_Misc.m_MotionVolumesBox.empty() ) { std::stringstream stream( m_Parameters.m_Misc.m_MotionVolumesBox ); std::vector numbers; int number = std::numeric_limits::max(); while( stream >> number ) { if( number < std::numeric_limits::max() ) { numbers.push_back( number ); } } // If a list of negative numbers is given: if( *(std::min_element( numbers.begin(), numbers.end() )) < 0 && *(std::max_element( numbers.begin(), numbers.end() )) <= 0 ) // cave: -0 == +0 { for ( size_t i=0; i < m_Parameters.m_SignalGen.GetNumVolumes(); ++i ) { m_Parameters.m_SignalGen.m_MotionVolumes.push_back( true ); } // set all true except those given. for( auto iter = std::begin( numbers ); iter != std::end( numbers ); ++iter ) { if ( -(*iter) < (int)m_Parameters.m_SignalGen.GetNumVolumes() && -(*iter) >= 0 ) { m_Parameters.m_SignalGen.m_MotionVolumes.at( -(*iter) ) = false; } } MITK_DEBUG << "QmitkFiberfoxView.cpp: Case list of negative numbers."; } // If a list of positive numbers is given: else if( *(std::min_element( numbers.begin(), numbers.end() )) >= 0 && *(std::max_element( numbers.begin(), numbers.end() )) >= 0 ) { for ( size_t i=0; i < m_Parameters.m_SignalGen.GetNumVolumes(); ++i ) { m_Parameters.m_SignalGen.m_MotionVolumes.push_back( false ); } // set all false except those given. for( auto iter = std::begin( numbers ); iter != std::end( numbers ); ++iter ) { if ( *iter < (int)m_Parameters.m_SignalGen.GetNumVolumes() && *iter >= 0 ) { m_Parameters.m_SignalGen.m_MotionVolumes.at( *iter ) = true; } } MITK_DEBUG << "QmitkFiberfoxView.cpp: Case list of positive numbers."; } else { MITK_ERROR << "QmitkFiberfoxView.cpp: Inconsistent list of numbers in m_MotionVolumesBox."; } } else { m_Parameters.m_Misc.m_MotionVolumesBox = ""; // set empty. m_Controls->m_MotionVolumesBox->setText(""); for (unsigned int i=0; im_AcquisitionTypeBox->currentIndex(); m_Parameters.m_SignalGen.m_CoilSensitivityProfile = (SignalGenerationParameters::CoilSensitivityProfile)m_Controls->m_CoilSensBox->currentIndex(); m_Parameters.m_SignalGen.m_NumberOfCoils = m_Controls->m_NumCoilsBox->value(); m_Parameters.m_SignalGen.m_PartialFourier = m_Controls->m_PartialFourier->value(); m_Parameters.m_SignalGen.m_ReversePhase = m_Controls->m_ReversePhaseBox->isChecked(); m_Parameters.m_SignalGen.m_tLine = m_Controls->m_LineReadoutTimeBox->value(); m_Parameters.m_SignalGen.m_tInhom = m_Controls->m_T2starBox->value(); m_Parameters.m_SignalGen.m_EchoTrainLength = m_Controls->m_EtlBox->value(); m_Parameters.m_SignalGen.m_tEcho = m_Controls->m_TEbox->value(); m_Parameters.m_SignalGen.m_tRep = m_Controls->m_TRbox->value(); m_Parameters.m_SignalGen.m_tInv = m_Controls->m_TIbox->value(); m_Parameters.m_SignalGen.m_DoDisablePartialVolume = m_Controls->m_EnforcePureFiberVoxelsBox->isChecked(); m_Parameters.m_SignalGen.m_AxonRadius = m_Controls->m_FiberRadius->value(); m_Parameters.m_SignalGen.m_SignalScale = m_Controls->m_SignalScaleBox->value(); // double voxelVolume = m_Parameters.m_SignalGen.m_ImageSpacing[0] // * m_Parameters.m_SignalGen.m_ImageSpacing[1] // * m_Parameters.m_SignalGen.m_ImageSpacing[2]; // if ( m_Parameters.m_SignalGen.m_SignalScale*voxelVolume > itk::NumericTraits::max()*0.75 ) // { // m_Parameters.m_SignalGen.m_SignalScale = itk::NumericTraits::max()*0.75/voxelVolume; // m_Controls->m_SignalScaleBox->setValue(m_Parameters.m_SignalGen.m_SignalScale); // QMessageBox::information( nullptr, "Warning", // "Maximum signal exceeding data type limits. Automatically adjusted to " // + QString::number(m_Parameters.m_SignalGen.m_SignalScale) // + " to obtain a maximum signal of 75% of the data type maximum." // " Relaxation and other effects that affect the signal intensities are not accounted for."); // } // Noise m_Parameters.m_Misc.m_DoAddNoise = m_Controls->m_AddNoise->isChecked(); m_Parameters.m_SignalGen.m_NoiseVariance = m_Controls->m_NoiseLevel->value(); if (m_Controls->m_AddNoise->isChecked()) { switch (m_Controls->m_NoiseDistributionBox->currentIndex()) { case 0: { if (m_Parameters.m_SignalGen.m_NoiseVariance>0) { m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; m_Parameters.m_Misc.m_ArtifactModelString += "_COMPLEX-GAUSSIAN-"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Noise-Distribution", StringProperty::New("Complex Gaussian")); } break; } case 1: { if (m_Parameters.m_SignalGen.m_NoiseVariance>0) { m_Parameters.m_NoiseModel = std::make_shared< mitk::RicianNoiseModel >(); m_Parameters.m_Misc.m_ArtifactModelString += "_RICIAN-"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Noise-Distribution", StringProperty::New("Rician")); m_Parameters.m_NoiseModel->SetNoiseVariance(m_Parameters.m_SignalGen.m_NoiseVariance); } break; } case 2: { if (m_Parameters.m_SignalGen.m_NoiseVariance>0) { m_Parameters.m_NoiseModel = std::make_shared< mitk::ChiSquareNoiseModel >(); m_Parameters.m_Misc.m_ArtifactModelString += "_CHISQUARED-"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Noise-Distribution", StringProperty::New("Chi-squared")); m_Parameters.m_NoiseModel->SetNoiseVariance(m_Parameters.m_SignalGen.m_NoiseVariance); } break; } default: { if (m_Parameters.m_SignalGen.m_NoiseVariance>0) { m_Parameters.m_SignalGen.m_SimulateKspaceAcquisition = true; m_Parameters.m_Misc.m_ArtifactModelString += "_COMPLEX-GAUSSIAN-"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Noise-Distribution", StringProperty::New("Complex Gaussian")); } break; } } if (m_Parameters.m_SignalGen.m_NoiseVariance>0) { m_Parameters.m_Misc.m_ArtifactModelString += QString::number(m_Parameters.m_SignalGen.m_NoiseVariance).toStdString(); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Noise-Variance", DoubleProperty::New(m_Parameters.m_SignalGen.m_NoiseVariance)); } } // signal models { // compartment 1 switch (m_Controls->m_Compartment1Box->currentIndex()) { case 0: { mitk::StickModel* model = new mitk::StickModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(m_Parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_StickWidget1->GetD()); model->SetT2(m_Controls->m_StickWidget1->GetT2()); model->SetT1(m_Controls->m_StickWidget1->GetT1()); model->m_CompartmentId = 1; m_Parameters.m_FiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Stick"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Description", StringProperty::New("Intra-axonal compartment") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Model", StringProperty::New("Stick") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D", DoubleProperty::New(m_Controls->m_StickWidget1->GetD()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.T2", DoubleProperty::New(model->GetT2()) ); break; } case 1: { mitk::TensorModel* model = new mitk::TensorModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(m_Parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity1(m_Controls->m_ZeppelinWidget1->GetD1()); model->SetDiffusivity2(m_Controls->m_ZeppelinWidget1->GetD2()); model->SetDiffusivity3(m_Controls->m_ZeppelinWidget1->GetD2()); model->SetT2(m_Controls->m_ZeppelinWidget1->GetT2()); model->SetT1(m_Controls->m_ZeppelinWidget1->GetT1()); model->m_CompartmentId = 1; m_Parameters.m_FiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Zeppelin"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Description", StringProperty::New("Intra-axonal compartment") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Model", StringProperty::New("Zeppelin") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D1", DoubleProperty::New(m_Controls->m_ZeppelinWidget1->GetD1()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D2", DoubleProperty::New(m_Controls->m_ZeppelinWidget1->GetD2()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.T2", DoubleProperty::New(model->GetT2()) ); break; } case 2: { mitk::TensorModel* model = new mitk::TensorModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(m_Parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity1(m_Controls->m_TensorWidget1->GetD1()); model->SetDiffusivity2(m_Controls->m_TensorWidget1->GetD2()); model->SetDiffusivity3(m_Controls->m_TensorWidget1->GetD3()); model->SetT2(m_Controls->m_TensorWidget1->GetT2()); model->SetT1(m_Controls->m_TensorWidget1->GetT1()); model->m_CompartmentId = 1; m_Parameters.m_FiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Tensor"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Description", StringProperty::New("Intra-axonal compartment") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Model", StringProperty::New("Tensor") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D1", DoubleProperty::New(m_Controls->m_TensorWidget1->GetD1()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D2", DoubleProperty::New(m_Controls->m_TensorWidget1->GetD2()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.D3", DoubleProperty::New(m_Controls->m_TensorWidget1->GetD3()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.T2", DoubleProperty::New(model->GetT2()) ); break; } case 3: { mitk::RawShModel* model = new mitk::RawShModel(); m_Parameters.m_SignalGen.m_DoSimulateRelaxation = false; model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetMaxNumKernels(m_Controls->m_PrototypeWidget1->GetNumberOfSamples()); model->SetFaRange(m_Controls->m_PrototypeWidget1->GetMinFa(), m_Controls->m_PrototypeWidget1->GetMaxFa()); model->SetAdcRange(m_Controls->m_PrototypeWidget1->GetMinAdc(), m_Controls->m_PrototypeWidget1->GetMaxAdc()); model->m_CompartmentId = 1; m_Parameters.m_FiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Prototype"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Description", StringProperty::New("Intra-axonal compartment") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment1.Model", StringProperty::New("Prototype") ); break; } } if (m_Controls->m_Comp1VolumeFraction->GetSelectedNode().IsNotNull()) { mitk::DataNode::Pointer volumeNode = m_Controls->m_Comp1VolumeFraction->GetSelectedNode(); ItkDoubleImgType::Pointer comp1VolumeImage = ItkDoubleImgType::New(); mitk::Image* img = dynamic_cast(volumeNode->GetData()); CastToItkImage< ItkDoubleImgType >(img, comp1VolumeImage); m_Parameters.m_FiberModelList.back()->SetVolumeFractionImage(comp1VolumeImage); } // compartment 2 switch (m_Controls->m_Compartment2Box->currentIndex()) { case 0: break; case 1: { mitk::StickModel* model = new mitk::StickModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(m_Parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_StickWidget2->GetD()); model->SetT2(m_Controls->m_StickWidget2->GetT2()); model->SetT1(m_Controls->m_StickWidget2->GetT1()); model->m_CompartmentId = 2; m_Parameters.m_FiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Stick"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Description", StringProperty::New("Inter-axonal compartment") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Model", StringProperty::New("Stick") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D", DoubleProperty::New(m_Controls->m_StickWidget2->GetD()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.T2", DoubleProperty::New(model->GetT2()) ); break; } case 2: { mitk::TensorModel* model = new mitk::TensorModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(m_Parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity1(m_Controls->m_ZeppelinWidget2->GetD1()); model->SetDiffusivity2(m_Controls->m_ZeppelinWidget2->GetD2()); model->SetDiffusivity3(m_Controls->m_ZeppelinWidget2->GetD2()); model->SetT2(m_Controls->m_ZeppelinWidget2->GetT2()); model->SetT1(m_Controls->m_ZeppelinWidget2->GetT1()); model->m_CompartmentId = 2; m_Parameters.m_FiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Zeppelin"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Description", StringProperty::New("Inter-axonal compartment") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Model", StringProperty::New("Zeppelin") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D1", DoubleProperty::New(m_Controls->m_ZeppelinWidget2->GetD1()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D2", DoubleProperty::New(m_Controls->m_ZeppelinWidget2->GetD2()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.T2", DoubleProperty::New(model->GetT2()) ); break; } case 3: { mitk::TensorModel* model = new mitk::TensorModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(m_Parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity1(m_Controls->m_TensorWidget2->GetD1()); model->SetDiffusivity2(m_Controls->m_TensorWidget2->GetD2()); model->SetDiffusivity3(m_Controls->m_TensorWidget2->GetD3()); model->SetT2(m_Controls->m_TensorWidget2->GetT2()); model->SetT1(m_Controls->m_TensorWidget2->GetT1()); model->m_CompartmentId = 2; m_Parameters.m_FiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Tensor"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Description", StringProperty::New("Inter-axonal compartment") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.Model", StringProperty::New("Tensor") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D1", DoubleProperty::New(m_Controls->m_TensorWidget2->GetD1()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D2", DoubleProperty::New(m_Controls->m_TensorWidget2->GetD2()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.D3", DoubleProperty::New(m_Controls->m_TensorWidget2->GetD3()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment2.T2", DoubleProperty::New(model->GetT2()) ); break; } } if (m_Controls->m_Comp2VolumeFraction->GetSelectedNode().IsNotNull() && m_Parameters.m_FiberModelList.size()==2) { mitk::DataNode::Pointer volumeNode = m_Controls->m_Comp2VolumeFraction->GetSelectedNode(); ItkDoubleImgType::Pointer comp1VolumeImage = ItkDoubleImgType::New(); mitk::Image* img = dynamic_cast(volumeNode->GetData()); CastToItkImage< ItkDoubleImgType >(img, comp1VolumeImage); m_Parameters.m_FiberModelList.back()->SetVolumeFractionImage(comp1VolumeImage); } // compartment 3 switch (m_Controls->m_Compartment3Box->currentIndex()) { case 0: { mitk::BallModel* model = new mitk::BallModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(m_Parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_BallWidget1->GetD()); model->SetT2(m_Controls->m_BallWidget1->GetT2()); model->SetT1(m_Controls->m_BallWidget1->GetT1()); model->m_CompartmentId = 3; m_Parameters.m_NonFiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Ball"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Description", StringProperty::New("Extra-axonal compartment 1") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Model", StringProperty::New("Ball") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.D", DoubleProperty::New(m_Controls->m_BallWidget1->GetD()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.T2", DoubleProperty::New(model->GetT2()) ); break; } case 1: { mitk::AstroStickModel* model = new mitk::AstroStickModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(m_Parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_AstrosticksWidget1->GetD()); model->SetT2(m_Controls->m_AstrosticksWidget1->GetT2()); model->SetT1(m_Controls->m_AstrosticksWidget1->GetT1()); model->SetRandomizeSticks(m_Controls->m_AstrosticksWidget1->GetRandomizeSticks()); model->m_CompartmentId = 3; m_Parameters.m_NonFiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Astrosticks"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Description", StringProperty::New("Extra-axonal compartment 1") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Model", StringProperty::New("Astrosticks") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.D", DoubleProperty::New(m_Controls->m_AstrosticksWidget1->GetD()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.T2", DoubleProperty::New(model->GetT2()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.RandomSticks", BoolProperty::New(m_Controls->m_AstrosticksWidget1->GetRandomizeSticks()) ); break; } case 2: { mitk::DotModel* model = new mitk::DotModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetT2(m_Controls->m_DotWidget1->GetT2()); model->SetT1(m_Controls->m_DotWidget1->GetT1()); model->m_CompartmentId = 3; m_Parameters.m_NonFiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Dot"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Description", StringProperty::New("Extra-axonal compartment 1") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Model", StringProperty::New("Dot") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.T2", DoubleProperty::New(model->GetT2()) ); break; } case 3: { mitk::RawShModel* model = new mitk::RawShModel(); m_Parameters.m_SignalGen.m_DoSimulateRelaxation = false; model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetMaxNumKernels(m_Controls->m_PrototypeWidget3->GetNumberOfSamples()); model->SetFaRange(m_Controls->m_PrototypeWidget3->GetMinFa(), m_Controls->m_PrototypeWidget3->GetMaxFa()); model->SetAdcRange(m_Controls->m_PrototypeWidget3->GetMinAdc(), m_Controls->m_PrototypeWidget3->GetMaxAdc()); model->m_CompartmentId = 3; m_Parameters.m_NonFiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Prototype"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Description", StringProperty::New("Extra-axonal compartment 1") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment3.Model", StringProperty::New("Prototype") ); break; } } if (m_Controls->m_Comp3VolumeFraction->GetSelectedNode().IsNotNull()) { mitk::DataNode::Pointer volumeNode = m_Controls->m_Comp3VolumeFraction->GetSelectedNode(); ItkDoubleImgType::Pointer comp1VolumeImage = ItkDoubleImgType::New(); mitk::Image* img = dynamic_cast(volumeNode->GetData()); CastToItkImage< ItkDoubleImgType >(img, comp1VolumeImage); m_Parameters.m_NonFiberModelList.back()->SetVolumeFractionImage(comp1VolumeImage); } switch (m_Controls->m_Compartment4Box->currentIndex()) { case 0: break; case 1: { mitk::BallModel* model = new mitk::BallModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(m_Parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_BallWidget2->GetD()); model->SetT2(m_Controls->m_BallWidget2->GetT2()); model->SetT1(m_Controls->m_BallWidget2->GetT1()); model->m_CompartmentId = 4; m_Parameters.m_NonFiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Ball"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Description", StringProperty::New("Extra-axonal compartment 2") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Model", StringProperty::New("Ball") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.D", DoubleProperty::New(m_Controls->m_BallWidget2->GetD()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.T2", DoubleProperty::New(model->GetT2()) ); break; } case 2: { mitk::AstroStickModel* model = new mitk::AstroStickModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetBvalue(m_Parameters.m_SignalGen.GetBvalue()); model->SetDiffusivity(m_Controls->m_AstrosticksWidget2->GetD()); model->SetT2(m_Controls->m_AstrosticksWidget2->GetT2()); model->SetT1(m_Controls->m_AstrosticksWidget2->GetT1()); model->SetRandomizeSticks(m_Controls->m_AstrosticksWidget2->GetRandomizeSticks()); model->m_CompartmentId = 4; m_Parameters.m_NonFiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Astrosticks"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Description", StringProperty::New("Extra-axonal compartment 2") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Model", StringProperty::New("Astrosticks") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.D", DoubleProperty::New(m_Controls->m_AstrosticksWidget2->GetD()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.T2", DoubleProperty::New(model->GetT2()) ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.RandomSticks", BoolProperty::New(m_Controls->m_AstrosticksWidget2->GetRandomizeSticks()) ); break; } case 3: { mitk::DotModel* model = new mitk::DotModel(); model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetT2(m_Controls->m_DotWidget2->GetT2()); model->SetT1(m_Controls->m_DotWidget2->GetT1()); model->m_CompartmentId = 4; m_Parameters.m_NonFiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Dot"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Description", StringProperty::New("Extra-axonal compartment 2") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Model", StringProperty::New("Dot") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.T2", DoubleProperty::New(model->GetT2()) ); break; } case 4: { mitk::RawShModel* model = new mitk::RawShModel(); m_Parameters.m_SignalGen.m_DoSimulateRelaxation = false; model->SetGradientList(m_Parameters.m_SignalGen.GetGradientDirections()); model->SetMaxNumKernels(m_Controls->m_PrototypeWidget4->GetNumberOfSamples()); model->SetFaRange(m_Controls->m_PrototypeWidget4->GetMinFa(), m_Controls->m_PrototypeWidget4->GetMaxFa()); model->SetAdcRange(m_Controls->m_PrototypeWidget4->GetMinAdc(), m_Controls->m_PrototypeWidget4->GetMaxAdc()); model->m_CompartmentId = 4; m_Parameters.m_NonFiberModelList.push_back(model); m_Parameters.m_Misc.m_SignalModelString += "Prototype"; m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Description", StringProperty::New("Extra-axonal compartment 2") ); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Compartment4.Model", StringProperty::New("Prototype") ); break; } } if (m_Controls->m_Comp4VolumeFraction->GetSelectedNode().IsNotNull() && m_Parameters.m_NonFiberModelList.size()==2) { mitk::DataNode::Pointer volumeNode = m_Controls->m_Comp4VolumeFraction->GetSelectedNode(); ItkDoubleImgType::Pointer compVolumeImage = ItkDoubleImgType::New(); mitk::Image* img = dynamic_cast(volumeNode->GetData()); CastToItkImage< ItkDoubleImgType >(img, compVolumeImage); m_Parameters.m_NonFiberModelList.back()->SetVolumeFractionImage(compVolumeImage); } } m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.SignalScale", IntProperty::New(m_Parameters.m_SignalGen.m_SignalScale)); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.FiberRadius", IntProperty::New(m_Parameters.m_SignalGen.m_AxonRadius)); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Tinhom", DoubleProperty::New(m_Parameters.m_SignalGen.m_tInhom)); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.echoTrainLength", IntProperty::New(m_Parameters.m_SignalGen.m_EchoTrainLength)); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Tline", DoubleProperty::New(m_Parameters.m_SignalGen.m_tLine)); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.TE", DoubleProperty::New(m_Parameters.m_SignalGen.m_tEcho)); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.b-value", DoubleProperty::New(m_Parameters.m_SignalGen.GetBvalue())); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.NoPartialVolume", BoolProperty::New(m_Parameters.m_SignalGen.m_DoDisablePartialVolume)); m_Parameters.m_Misc.m_ResultNode->AddProperty("Fiberfox.Relaxation", BoolProperty::New(m_Parameters.m_SignalGen.m_DoSimulateRelaxation)); m_Parameters.m_Misc.m_ResultNode->AddProperty("binary", BoolProperty::New(false)); } void QmitkFiberfoxView::SaveParameters(QString filename) { UpdateParametersFromGui(); std::vector< int > bVals = m_Parameters.m_SignalGen.GetBvalues(); std::cout << "b-values: "; for (auto v : bVals) std::cout << v << " "; std::cout << std::endl; bool ok = true; bool first = true; bool dosampling = false; mitk::Image::Pointer diffImg = nullptr; itk::Image< itk::DiffusionTensor3D< double >, 3 >::Pointer tensorImage = nullptr; const int shOrder = 2; typedef itk::AnalyticalDiffusionQballReconstructionImageFilter QballFilterType; QballFilterType::CoefficientImageType::Pointer itkFeatureImage = nullptr; ItkDoubleImgType::Pointer adcImage = nullptr; for (unsigned int i=0; i* model = nullptr; if (i* >(m_Parameters.m_FiberModelList.at(i)); } else { model = dynamic_cast< mitk::RawShModel<>* >(m_Parameters.m_NonFiberModelList.at(i-m_Parameters.m_FiberModelList.size())); } if ( model!=nullptr && model->GetNumberOfKernels() <= 0 ) { if (first==true) { if ( QMessageBox::question(nullptr, "Prototype signal sampling", "Do you want to sample prototype signals from the selected diffusion-weighted imag and save them?", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes ) dosampling = true; first = false; if ( dosampling && (m_Controls->m_TemplateComboBox->GetSelectedNode().IsNull() || !mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()) ) ) ) { QMessageBox::information(nullptr, "Parameter file not saved", "No diffusion-weighted image selected to sample signal from."); return; } else if (dosampling) { diffImg = dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()); typedef itk::DiffusionTensor3DReconstructionImageFilter< short, short, double > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer filter = TensorReconstructionImageFilterType::New(); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(diffImg, itkVectorImagePointer); filter->SetBValue(mitk::DiffusionPropertyHelper::GetReferenceBValue(diffImg)); filter->SetGradientImage(mitk::DiffusionPropertyHelper::GetGradientContainer(diffImg), itkVectorImagePointer ); filter->Update(); tensorImage = filter->GetOutput(); QballFilterType::Pointer qballfilter = QballFilterType::New(); qballfilter->SetBValue(mitk::DiffusionPropertyHelper::GetReferenceBValue(diffImg)); qballfilter->SetGradientImage(mitk::DiffusionPropertyHelper::GetGradientContainer(diffImg), itkVectorImagePointer ); qballfilter->SetLambda(0.006); qballfilter->SetNormalizationMethod(QballFilterType::QBAR_RAW_SIGNAL); qballfilter->Update(); itkFeatureImage = qballfilter->GetCoefficientImage(); itk::AdcImageFilter< short, double >::Pointer adcFilter = itk::AdcImageFilter< short, double >::New(); adcFilter->SetInput( itkVectorImagePointer ); adcFilter->SetGradientDirections(mitk::DiffusionPropertyHelper::GetGradientContainer(diffImg)); adcFilter->SetB_value(mitk::DiffusionPropertyHelper::GetReferenceBValue(diffImg)); adcFilter->Update(); adcImage = adcFilter->GetOutput(); } } typedef itk::DiffusionTensor3DReconstructionImageFilter< short, short, double > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer filter = TensorReconstructionImageFilterType::New(); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(diffImg, itkVectorImagePointer); filter->SetBValue(mitk::DiffusionPropertyHelper::GetReferenceBValue(diffImg)); filter->SetGradientImage(mitk::DiffusionPropertyHelper::GetGradientContainer(diffImg), itkVectorImagePointer ); filter->Update(); tensorImage = filter->GetOutput(); QballFilterType::Pointer qballfilter = QballFilterType::New(); qballfilter->SetBValue(mitk::DiffusionPropertyHelper::GetReferenceBValue(diffImg)); qballfilter->SetGradientImage(mitk::DiffusionPropertyHelper::GetGradientContainer(diffImg), itkVectorImagePointer ); qballfilter->SetLambda(0.006); qballfilter->SetNormalizationMethod(QballFilterType::QBAR_RAW_SIGNAL); qballfilter->Update(); itkFeatureImage = qballfilter->GetCoefficientImage(); itk::AdcImageFilter< short, double >::Pointer adcFilter = itk::AdcImageFilter< short, double >::New(); adcFilter->SetInput( itkVectorImagePointer ); adcFilter->SetGradientDirections(mitk::DiffusionPropertyHelper::GetGradientContainer(diffImg)); adcFilter->SetB_value(mitk::DiffusionPropertyHelper::GetReferenceBValue(diffImg)); adcFilter->Update(); adcImage = adcFilter->GetOutput(); if (dosampling && diffImg.IsNotNull()) { ok = model->SampleKernels(diffImg, m_Parameters.m_SignalGen.m_MaskImage, tensorImage, itkFeatureImage, adcImage); if (!ok) { QMessageBox::information( nullptr, "Parameter file not saved", "No valid prototype signals could be sampled."); return; } } } } m_Parameters.SaveParameters(filename.toStdString()); m_ParameterFile = filename; } void QmitkFiberfoxView::SaveParameters() { QString filename = QFileDialog::getSaveFileName( 0, tr("Save Parameters"), m_ParameterFile, tr("Fiberfox Parameters (*.ffp)") ); SaveParameters(filename); } void QmitkFiberfoxView::LoadParameters() { QString filename = QFileDialog::getOpenFileName(0, tr("Load Parameters"), QString(itksys::SystemTools::GetFilenamePath(m_ParameterFile.toStdString()).c_str()), tr("Fiberfox Parameters (*.ffp)") ); if(filename.isEmpty() || filename.isNull()) return; m_ParameterFile = filename; m_Parameters.LoadParameters(filename.toStdString()); if (m_Parameters.m_MissingTags.size()>0) { QString missing("Parameter file might be corrupted. The following parameters could not be read: "); missing += QString(m_Parameters.m_MissingTags.c_str()); missing += "\nDefault values have been assigned to the missing parameters."; QMessageBox::information( nullptr, "Warning!", missing); } // image generation parameters m_Controls->m_SizeX->setValue(m_Parameters.m_SignalGen.m_ImageRegion.GetSize(0)); m_Controls->m_SizeY->setValue(m_Parameters.m_SignalGen.m_ImageRegion.GetSize(1)); m_Controls->m_SizeZ->setValue(m_Parameters.m_SignalGen.m_ImageRegion.GetSize(2)); m_Controls->m_SpacingX->setValue(m_Parameters.m_SignalGen.m_ImageSpacing[0]); m_Controls->m_SpacingY->setValue(m_Parameters.m_SignalGen.m_ImageSpacing[1]); m_Controls->m_SpacingZ->setValue(m_Parameters.m_SignalGen.m_ImageSpacing[2]); m_Controls->m_NumGradientsBox->setValue(m_Parameters.m_SignalGen.GetNumWeightedVolumes()); m_Controls->m_BvalueBox->setValue(m_Parameters.m_SignalGen.GetBvalue()); m_Controls->m_SignalScaleBox->setValue(m_Parameters.m_SignalGen.m_SignalScale); m_Controls->m_TEbox->setValue(m_Parameters.m_SignalGen.m_tEcho); m_Controls->m_LineReadoutTimeBox->setValue(m_Parameters.m_SignalGen.m_tLine); m_Controls->m_T2starBox->setValue(m_Parameters.m_SignalGen.m_tInhom); m_Controls->m_EtlBox->setValue(m_Parameters.m_SignalGen.m_EchoTrainLength); m_Controls->m_FiberRadius->setValue(m_Parameters.m_SignalGen.m_AxonRadius); m_Controls->m_RelaxationBox->setChecked(m_Parameters.m_SignalGen.m_DoSimulateRelaxation); m_Controls->m_EnforcePureFiberVoxelsBox->setChecked(m_Parameters.m_SignalGen.m_DoDisablePartialVolume); m_Controls->m_ReversePhaseBox->setChecked(m_Parameters.m_SignalGen.m_ReversePhase); m_Controls->m_PartialFourier->setValue(m_Parameters.m_SignalGen.m_PartialFourier); m_Controls->m_TRbox->setValue(m_Parameters.m_SignalGen.m_tRep); m_Controls->m_TIbox->setValue(m_Parameters.m_SignalGen.m_tInv); m_Controls->m_NumCoilsBox->setValue(m_Parameters.m_SignalGen.m_NumberOfCoils); m_Controls->m_CoilSensBox->setCurrentIndex(m_Parameters.m_SignalGen.m_CoilSensitivityProfile); m_Controls->m_AcquisitionTypeBox->setCurrentIndex(m_Parameters.m_SignalGen.m_AcquisitionType); if (!m_Parameters.m_Misc.m_BvalsFile.empty()) { m_Controls->m_UseBvalsBvecsBox->setChecked(true); m_Controls->m_LoadBvalsEdit->setText(QString(m_Parameters.m_Misc.m_BvalsFile.c_str())); } else m_Controls->m_LoadBvalsEdit->setText("-"); if (!m_Parameters.m_Misc.m_BvecsFile.empty()) { m_Controls->m_UseBvalsBvecsBox->setChecked(true); m_Controls->m_LoadBvecsEdit->setText(QString(m_Parameters.m_Misc.m_BvecsFile.c_str())); } else m_Controls->m_LoadBvecsEdit->setText("-"); if (m_Parameters.m_NoiseModel!=nullptr) { m_Controls->m_AddNoise->setChecked(m_Parameters.m_Misc.m_DoAddNoise); if (dynamic_cast*>(m_Parameters.m_NoiseModel.get())) { m_Controls->m_NoiseDistributionBox->setCurrentIndex(0); } else if (dynamic_cast*>(m_Parameters.m_NoiseModel.get())) { m_Controls->m_NoiseDistributionBox->setCurrentIndex(1); } m_Controls->m_NoiseLevel->setValue(m_Parameters.m_NoiseModel->GetNoiseVariance()); } else { m_Controls->m_AddNoise->setChecked(m_Parameters.m_Misc.m_DoAddNoise); m_Controls->m_NoiseLevel->setValue(m_Parameters.m_SignalGen.m_NoiseVariance); } - m_Controls->m_VolumeFractionsBox->setChecked(m_Parameters.m_Misc.m_CheckOutputVolumeFractionsBox); + m_Controls->m_VolumeFractionsBox->setChecked(m_Parameters.m_Misc.m_OutputAdditionalImages); m_Controls->m_AdvancedOptionsBox_2->setChecked(m_Parameters.m_Misc.m_CheckAdvancedSignalOptionsBox); m_Controls->m_AddGhosts->setChecked(m_Parameters.m_Misc.m_DoAddGhosts); m_Controls->m_AddAliasing->setChecked(m_Parameters.m_Misc.m_DoAddAliasing); m_Controls->m_AddDistortions->setChecked(m_Parameters.m_Misc.m_DoAddDistortions); m_Controls->m_AddSpikes->setChecked(m_Parameters.m_Misc.m_DoAddSpikes); m_Controls->m_AddEddy->setChecked(m_Parameters.m_Misc.m_DoAddEddyCurrents); m_Controls->m_AddDrift->setChecked(m_Parameters.m_SignalGen.m_DoAddDrift); m_Controls->m_kOffsetBox->setValue(m_Parameters.m_SignalGen.m_KspaceLineOffset); m_Controls->m_WrapBox->setValue(100*(1-m_Parameters.m_SignalGen.m_CroppingFactor)); m_Controls->m_DriftFactor->setValue(100*m_Parameters.m_SignalGen.m_Drift); m_Controls->m_SpikeNumBox->setValue(m_Parameters.m_SignalGen.m_Spikes); m_Controls->m_SpikeScaleBox->setValue(m_Parameters.m_SignalGen.m_SpikeAmplitude); m_Controls->m_EddyGradientStrength->setValue(m_Parameters.m_SignalGen.m_EddyStrength); m_Controls->m_AddGibbsRinging->setChecked(m_Parameters.m_SignalGen.m_DoAddGibbsRinging); m_Controls->m_ZeroRinging->setValue(m_Parameters.m_SignalGen.m_ZeroRinging); m_Controls->m_AddMotion->setChecked(m_Parameters.m_SignalGen.m_DoAddMotion); m_Controls->m_RandomMotion->setChecked(m_Parameters.m_SignalGen.m_DoRandomizeMotion); m_Controls->m_MotionVolumesBox->setText(QString(m_Parameters.m_Misc.m_MotionVolumesBox.c_str())); m_Controls->m_MaxTranslationBoxX->setValue(m_Parameters.m_SignalGen.m_Translation[0]); m_Controls->m_MaxTranslationBoxY->setValue(m_Parameters.m_SignalGen.m_Translation[1]); m_Controls->m_MaxTranslationBoxZ->setValue(m_Parameters.m_SignalGen.m_Translation[2]); m_Controls->m_MaxRotationBoxX->setValue(m_Parameters.m_SignalGen.m_Rotation[0]); m_Controls->m_MaxRotationBoxY->setValue(m_Parameters.m_SignalGen.m_Rotation[1]); m_Controls->m_MaxRotationBoxZ->setValue(m_Parameters.m_SignalGen.m_Rotation[2]); m_Controls->m_Compartment1Box->setCurrentIndex(0); m_Controls->m_Compartment2Box->setCurrentIndex(0); m_Controls->m_Compartment3Box->setCurrentIndex(0); m_Controls->m_Compartment4Box->setCurrentIndex(0); for (unsigned int i=0; i* signalModel = nullptr; if (iGetVolumeFractionImage().IsNotNull() ) { compVolNode = mitk::DataNode::New(); mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(signalModel->GetVolumeFractionImage().GetPointer()); image->SetVolume(signalModel->GetVolumeFractionImage()->GetBufferPointer()); compVolNode->SetData( image ); compVolNode->SetName("Compartment volume "+QString::number(signalModel->m_CompartmentId).toStdString()); GetDataStorage()->Add(compVolNode); } switch (signalModel->m_CompartmentId) { case 1: { if (compVolNode.IsNotNull()) m_Controls->m_Comp1VolumeFraction->SetSelectedNode(compVolNode); if (dynamic_cast*>(signalModel)) { mitk::StickModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_StickWidget1->SetT2(model->GetT2()); m_Controls->m_StickWidget1->SetT1(model->GetT1()); m_Controls->m_StickWidget1->SetD(model->GetDiffusivity()); m_Controls->m_Compartment1Box->setCurrentIndex(0); break; } else if (dynamic_cast*>(signalModel)) { mitk::TensorModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_TensorWidget1->SetT2(model->GetT2()); m_Controls->m_TensorWidget1->SetT1(model->GetT1()); m_Controls->m_TensorWidget1->SetD1(model->GetDiffusivity1()); m_Controls->m_TensorWidget1->SetD2(model->GetDiffusivity2()); m_Controls->m_TensorWidget1->SetD3(model->GetDiffusivity3()); m_Controls->m_Compartment1Box->setCurrentIndex(2); break; } else if (dynamic_cast*>(signalModel)) { mitk::RawShModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_PrototypeWidget1->SetNumberOfSamples(model->GetMaxNumKernels()); m_Controls->m_PrototypeWidget1->SetMinFa(model->GetFaRange().first); m_Controls->m_PrototypeWidget1->SetMaxFa(model->GetFaRange().second); m_Controls->m_PrototypeWidget1->SetMinAdc(model->GetAdcRange().first); m_Controls->m_PrototypeWidget1->SetMaxAdc(model->GetAdcRange().second); m_Controls->m_Compartment1Box->setCurrentIndex(3); break; } break; } case 2: { if (compVolNode.IsNotNull()) m_Controls->m_Comp2VolumeFraction->SetSelectedNode(compVolNode); if (dynamic_cast*>(signalModel)) { mitk::StickModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_StickWidget2->SetT2(model->GetT2()); m_Controls->m_StickWidget2->SetT1(model->GetT1()); m_Controls->m_StickWidget2->SetD(model->GetDiffusivity()); m_Controls->m_Compartment2Box->setCurrentIndex(1); break; } else if (dynamic_cast*>(signalModel)) { mitk::TensorModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_TensorWidget2->SetT2(model->GetT2()); m_Controls->m_TensorWidget2->SetT1(model->GetT1()); m_Controls->m_TensorWidget2->SetD1(model->GetDiffusivity1()); m_Controls->m_TensorWidget2->SetD2(model->GetDiffusivity2()); m_Controls->m_TensorWidget2->SetD3(model->GetDiffusivity3()); m_Controls->m_Compartment2Box->setCurrentIndex(3); break; } break; } case 3: { if (compVolNode.IsNotNull()) m_Controls->m_Comp3VolumeFraction->SetSelectedNode(compVolNode); if (dynamic_cast*>(signalModel)) { mitk::BallModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_BallWidget1->SetT2(model->GetT2()); m_Controls->m_BallWidget1->SetT1(model->GetT1()); m_Controls->m_BallWidget1->SetD(model->GetDiffusivity()); m_Controls->m_Compartment3Box->setCurrentIndex(0); break; } else if (dynamic_cast*>(signalModel)) { mitk::AstroStickModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_AstrosticksWidget1->SetT2(model->GetT2()); m_Controls->m_AstrosticksWidget1->SetT1(model->GetT1()); m_Controls->m_AstrosticksWidget1->SetD(model->GetDiffusivity()); m_Controls->m_AstrosticksWidget1->SetRandomizeSticks(model->GetRandomizeSticks()); m_Controls->m_Compartment3Box->setCurrentIndex(1); break; } else if (dynamic_cast*>(signalModel)) { mitk::DotModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_DotWidget1->SetT2(model->GetT2()); m_Controls->m_DotWidget1->SetT1(model->GetT1()); m_Controls->m_Compartment3Box->setCurrentIndex(2); break; } else if (dynamic_cast*>(signalModel)) { mitk::RawShModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_PrototypeWidget3->SetNumberOfSamples(model->GetMaxNumKernels()); m_Controls->m_PrototypeWidget3->SetMinFa(model->GetFaRange().first); m_Controls->m_PrototypeWidget3->SetMaxFa(model->GetFaRange().second); m_Controls->m_PrototypeWidget3->SetMinAdc(model->GetAdcRange().first); m_Controls->m_PrototypeWidget3->SetMaxAdc(model->GetAdcRange().second); m_Controls->m_Compartment3Box->setCurrentIndex(3); break; } break; } case 4: { if (compVolNode.IsNotNull()) m_Controls->m_Comp4VolumeFraction->SetSelectedNode(compVolNode); if (dynamic_cast*>(signalModel)) { mitk::BallModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_BallWidget2->SetT2(model->GetT2()); m_Controls->m_BallWidget2->SetT1(model->GetT1()); m_Controls->m_BallWidget2->SetD(model->GetDiffusivity()); m_Controls->m_Compartment4Box->setCurrentIndex(1); break; } else if (dynamic_cast*>(signalModel)) { mitk::AstroStickModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_AstrosticksWidget2->SetT2(model->GetT2()); m_Controls->m_AstrosticksWidget2->SetT1(model->GetT1()); m_Controls->m_AstrosticksWidget2->SetD(model->GetDiffusivity()); m_Controls->m_AstrosticksWidget2->SetRandomizeSticks(model->GetRandomizeSticks()); m_Controls->m_Compartment4Box->setCurrentIndex(2); break; } else if (dynamic_cast*>(signalModel)) { mitk::DotModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_DotWidget2->SetT2(model->GetT2()); m_Controls->m_DotWidget2->SetT1(model->GetT1()); m_Controls->m_Compartment4Box->setCurrentIndex(3); break; } else if (dynamic_cast*>(signalModel)) { mitk::RawShModel<>* model = dynamic_cast*>(signalModel); m_Controls->m_PrototypeWidget4->SetNumberOfSamples(model->GetMaxNumKernels()); m_Controls->m_PrototypeWidget4->SetMinFa(model->GetFaRange().first); m_Controls->m_PrototypeWidget4->SetMaxFa(model->GetFaRange().second); m_Controls->m_PrototypeWidget4->SetMinAdc(model->GetAdcRange().first); m_Controls->m_PrototypeWidget4->SetMaxAdc(model->GetAdcRange().second); m_Controls->m_Compartment4Box->setCurrentIndex(4); break; } break; } } } if ( m_Parameters.m_SignalGen.m_MaskImage ) { mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(m_Parameters.m_SignalGen.m_MaskImage.GetPointer()); image->SetVolume(m_Parameters.m_SignalGen.m_MaskImage->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("Tissue mask"); GetDataStorage()->Add(node); m_Controls->m_MaskComboBox->SetSelectedNode(node); } if ( m_Parameters.m_SignalGen.m_FrequencyMap ) { mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk(m_Parameters.m_SignalGen.m_FrequencyMap.GetPointer()); image->SetVolume(m_Parameters.m_SignalGen.m_FrequencyMap->GetBufferPointer()); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("Frequency map"); GetDataStorage()->Add(node); m_Controls->m_FrequencyMapBox->SetSelectedNode(node); } } void QmitkFiberfoxView::ShowAdvancedOptions(int state) { if (state) { m_Controls->m_AdvancedSignalOptionsFrame->setVisible(true); m_Controls->m_AdvancedOptionsBox_2->setChecked(true); } else { m_Controls->m_AdvancedSignalOptionsFrame->setVisible(false); m_Controls->m_AdvancedOptionsBox_2->setChecked(false); } } void QmitkFiberfoxView::Comp1ModelFrameVisibility(int index) { m_Controls->m_StickWidget1->setVisible(false); m_Controls->m_ZeppelinWidget1->setVisible(false); m_Controls->m_TensorWidget1->setVisible(false); m_Controls->m_PrototypeWidget1->setVisible(false); switch (index) { case 0: m_Controls->m_StickWidget1->setVisible(true); break; case 1: m_Controls->m_ZeppelinWidget1->setVisible(true); break; case 2: m_Controls->m_TensorWidget1->setVisible(true); break; case 3: m_Controls->m_PrototypeWidget1->setVisible(true); break; } } void QmitkFiberfoxView::Comp2ModelFrameVisibility(int index) { m_Controls->m_StickWidget2->setVisible(false); m_Controls->m_ZeppelinWidget2->setVisible(false); m_Controls->m_TensorWidget2->setVisible(false); m_Controls->m_Comp2FractionFrame->setVisible(false); switch (index) { case 0: break; case 1: m_Controls->m_StickWidget2->setVisible(true); m_Controls->m_Comp2FractionFrame->setVisible(true); break; case 2: m_Controls->m_ZeppelinWidget2->setVisible(true); m_Controls->m_Comp2FractionFrame->setVisible(true); break; case 3: m_Controls->m_TensorWidget2->setVisible(true); m_Controls->m_Comp2FractionFrame->setVisible(true); break; } } void QmitkFiberfoxView::Comp3ModelFrameVisibility(int index) { m_Controls->m_BallWidget1->setVisible(false); m_Controls->m_AstrosticksWidget1->setVisible(false); m_Controls->m_DotWidget1->setVisible(false); m_Controls->m_PrototypeWidget3->setVisible(false); switch (index) { case 0: m_Controls->m_BallWidget1->setVisible(true); break; case 1: m_Controls->m_AstrosticksWidget1->setVisible(true); break; case 2: m_Controls->m_DotWidget1->setVisible(true); break; case 3: m_Controls->m_PrototypeWidget3->setVisible(true); break; } } void QmitkFiberfoxView::Comp4ModelFrameVisibility(int index) { m_Controls->m_BallWidget2->setVisible(false); m_Controls->m_AstrosticksWidget2->setVisible(false); m_Controls->m_DotWidget2->setVisible(false); m_Controls->m_PrototypeWidget4->setVisible(false); m_Controls->m_Comp4FractionFrame->setVisible(false); switch (index) { case 0: break; case 1: m_Controls->m_BallWidget2->setVisible(true); m_Controls->m_Comp4FractionFrame->setVisible(true); break; case 2: m_Controls->m_AstrosticksWidget2->setVisible(true); m_Controls->m_Comp4FractionFrame->setVisible(true); break; case 3: m_Controls->m_DotWidget2->setVisible(true); m_Controls->m_Comp4FractionFrame->setVisible(true); break; case 4: m_Controls->m_PrototypeWidget4->setVisible(true); m_Controls->m_Comp4FractionFrame->setVisible(true); break; } } void QmitkFiberfoxView::OnAddMotion(int value) { if (value>0) m_Controls->m_MotionArtifactFrame->setVisible(true); else m_Controls->m_MotionArtifactFrame->setVisible(false); } void QmitkFiberfoxView::OnAddDrift(int value) { if (value>0) m_Controls->m_DriftFrame->setVisible(true); else m_Controls->m_DriftFrame->setVisible(false); } void QmitkFiberfoxView::OnAddAliasing(int value) { if (value>0) m_Controls->m_AliasingFrame->setVisible(true); else m_Controls->m_AliasingFrame->setVisible(false); } void QmitkFiberfoxView::OnAddSpikes(int value) { if (value>0) m_Controls->m_SpikeFrame->setVisible(true); else m_Controls->m_SpikeFrame->setVisible(false); } void QmitkFiberfoxView::OnAddEddy(int value) { if (value>0) m_Controls->m_EddyFrame->setVisible(true); else m_Controls->m_EddyFrame->setVisible(false); } void QmitkFiberfoxView::OnAddDistortions(int value) { if (value>0) m_Controls->m_DistortionsFrame->setVisible(true); else m_Controls->m_DistortionsFrame->setVisible(false); } void QmitkFiberfoxView::OnAddGhosts(int value) { if (value>0) m_Controls->m_GhostFrame->setVisible(true); else m_Controls->m_GhostFrame->setVisible(false); } void QmitkFiberfoxView::OnAddNoise(int value) { if (value>0) m_Controls->m_NoiseFrame->setVisible(true); else m_Controls->m_NoiseFrame->setVisible(false); } void QmitkFiberfoxView::OnAddRinging(int value) { if (value>0) m_Controls->m_ZeroRinging->setVisible(true); else m_Controls->m_ZeroRinging->setVisible(false); } QmitkFiberfoxView::GradientListType QmitkFiberfoxView::GenerateHalfShell(int NPoints) { NPoints *= 2; GradientListType pointshell; int numB0 = NPoints/20; if (numB0==0) numB0=1; GradientType g; g.Fill(0.0); for (int i=0; i theta; theta.set_size(NPoints); vnl_vector phi; phi.set_size(NPoints); double C = sqrt(4*itk::Math::pi); phi(0) = 0.0; phi(NPoints-1) = 0.0; for(int i=0; i0 && i std::vector > QmitkFiberfoxView::MakeGradientList() { std::vector > retval; vnl_matrix_fixed* U = itk::PointShell >::DistributePointShell(); // Add 0 vector for B0 int numB0 = ndirs/10; if (numB0==0) numB0=1; itk::Vector v; v.Fill(0.0); for (int i=0; i v; v[0] = U->get(0,i); v[1] = U->get(1,i); v[2] = U->get(2,i); retval.push_back(v); } return retval; } void QmitkFiberfoxView::GenerateImage() { if (m_Controls->m_FiberBundleComboBox->GetSelectedNode().IsNull() && !mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( m_Controls->m_TemplateComboBox->GetSelectedNode())) { mitk::Image::Pointer image = mitk::ImageGenerator::GenerateGradientImage( m_Controls->m_SizeX->value(), m_Controls->m_SizeY->value(), m_Controls->m_SizeZ->value(), m_Controls->m_SpacingX->value(), m_Controls->m_SpacingY->value(), m_Controls->m_SpacingZ->value()); mitk::Point3D origin; origin[0] = m_Controls->m_SpacingX->value()/2; origin[1] = m_Controls->m_SpacingY->value()/2; origin[2] = m_Controls->m_SpacingZ->value()/2; image->SetOrigin(origin); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData( image ); node->SetName("Dummy"); GetDataStorage()->Add(node); m_SelectedImageNode = node; mitk::BaseData::Pointer basedata = node->GetData(); if (basedata.IsNotNull()) { mitk::RenderingManager::GetInstance()->InitializeViews( basedata->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } UpdateGui(); QMessageBox::information(nullptr, "Template image generated", "You have selected no fiber bundle or diffusion-weighted image, which can be used to simulate a new diffusion-weighted image. A template image with the specified geometry has been generated that can be used to draw artificial fibers (see view 'Fiber Generator')."); } else if (m_Controls->m_FiberBundleComboBox->GetSelectedNode().IsNotNull()) SimulateImageFromFibers(m_Controls->m_FiberBundleComboBox->GetSelectedNode()); else if ( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( m_Controls->m_TemplateComboBox->GetSelectedNode()) ) SimulateForExistingDwi(m_Controls->m_TemplateComboBox->GetSelectedNode()); else QMessageBox::information(nullptr, "No image generated", "You have selected no fiber bundle or diffusion-weighted image, which can be used to simulate a new diffusion-weighted image."); } void QmitkFiberfoxView::SetFocus() { } void QmitkFiberfoxView::SimulateForExistingDwi(mitk::DataNode* imageNode) { bool isDiffusionImage( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(imageNode->GetData())) ); if ( !isDiffusionImage ) { return; } UpdateParametersFromGui(); mitk::Image::Pointer diffImg = dynamic_cast(imageNode->GetData()); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(diffImg, itkVectorImagePointer); m_TractsToDwiFilter = itk::TractsToDWIImageFilter< short >::New(); m_Parameters.m_Misc.m_ParentNode = imageNode; m_Parameters.m_SignalGen.m_SignalScale = 1; m_Parameters.m_Misc.m_ResultNode->SetName("b"+QString::number(m_Parameters.m_SignalGen.GetBvalue()).toStdString() +"_"+m_Parameters.m_Misc.m_SignalModelString +m_Parameters.m_Misc.m_ArtifactModelString); m_Parameters.ApplyDirectionMatrix(); m_TractsToDwiFilter->SetParameters(m_Parameters); m_TractsToDwiFilter->SetInputImage(itkVectorImagePointer); m_Thread.start(QThread::LowestPriority); } void QmitkFiberfoxView::SimulateImageFromFibers(mitk::DataNode* fiberNode) { mitk::FiberBundle::Pointer fiberBundle = dynamic_cast(fiberNode->GetData()); if (fiberBundle->GetNumFibers()<=0) { return; } UpdateParametersFromGui(); m_TractsToDwiFilter = itk::TractsToDWIImageFilter< short >::New(); m_Parameters.m_Misc.m_ParentNode = fiberNode; m_Parameters.m_Misc.m_ResultNode->SetName("b"+QString::number(m_Parameters.m_SignalGen.GetBvalue()).toStdString() +"_"+m_Parameters.m_Misc.m_SignalModelString +m_Parameters.m_Misc.m_ArtifactModelString); if ( m_Controls->m_TemplateComboBox->GetSelectedNode().IsNotNull() && mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast (m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()) ) ) { bool first = true; bool ok = true; mitk::Image::Pointer diffImg = dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()); itk::Image< itk::DiffusionTensor3D< double >, 3 >::Pointer tensorImage = nullptr; const int shOrder = 2; typedef itk::AnalyticalDiffusionQballReconstructionImageFilter QballFilterType; QballFilterType::CoefficientImageType::Pointer itkFeatureImage = nullptr; ItkDoubleImgType::Pointer adcImage = nullptr; for (unsigned int i=0; i* model = nullptr; if (i* >(m_Parameters.m_FiberModelList.at(i)); else model = dynamic_cast< mitk::RawShModel<>* >(m_Parameters.m_NonFiberModelList.at(i-m_Parameters.m_FiberModelList.size())); if (model!=0 && model->GetNumberOfKernels()<=0) { if (first==true) { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(diffImg, itkVectorImagePointer); typedef itk::DiffusionTensor3DReconstructionImageFilter< short, short, double > TensorReconstructionImageFilterType; TensorReconstructionImageFilterType::Pointer filter = TensorReconstructionImageFilterType::New(); filter->SetBValue(mitk::DiffusionPropertyHelper::GetReferenceBValue(diffImg)); filter->SetGradientImage(mitk::DiffusionPropertyHelper::GetGradientContainer(diffImg), itkVectorImagePointer ); filter->Update(); tensorImage = filter->GetOutput(); QballFilterType::Pointer qballfilter = QballFilterType::New(); qballfilter->SetGradientImage(mitk::DiffusionPropertyHelper::GetGradientContainer(diffImg), itkVectorImagePointer ); qballfilter->SetBValue(mitk::DiffusionPropertyHelper::GetReferenceBValue(diffImg)); qballfilter->SetLambda(0.006); qballfilter->SetNormalizationMethod(QballFilterType::QBAR_RAW_SIGNAL); qballfilter->Update(); itkFeatureImage = qballfilter->GetCoefficientImage(); itk::AdcImageFilter< short, double >::Pointer adcFilter = itk::AdcImageFilter< short, double >::New(); adcFilter->SetInput( itkVectorImagePointer ); adcFilter->SetGradientDirections(mitk::DiffusionPropertyHelper::GetGradientContainer(diffImg)); adcFilter->SetB_value(mitk::DiffusionPropertyHelper::GetReferenceBValue(diffImg)); adcFilter->Update(); adcImage = adcFilter->GetOutput(); } ok = model->SampleKernels(diffImg, m_Parameters.m_SignalGen.m_MaskImage, tensorImage, itkFeatureImage, adcImage); if (!ok) break; } } if (!ok) { QMessageBox::information( nullptr, "Simulation cancelled", "No valid prototype signals could be sampled."); return; } } else if ( m_Controls->m_Compartment1Box->currentIndex()==3 || m_Controls->m_Compartment3Box->currentIndex()==3 || m_Controls->m_Compartment4Box->currentIndex()==4 ) { QMessageBox::information( nullptr, "Simulation cancelled", "Prototype signal but no diffusion-weighted image selected to sample signal from."); return; } m_Parameters.ApplyDirectionMatrix(); m_TractsToDwiFilter->SetParameters(m_Parameters); m_TractsToDwiFilter->SetFiberBundle(fiberBundle); m_Thread.start(QThread::LowestPriority); } void QmitkFiberfoxView::SetBvalsEdit() { // SELECT FOLDER DIALOG std::string filename; filename = QFileDialog::getOpenFileName(nullptr, "Select bvals file", QString(filename.c_str())).toStdString(); if (filename.empty()) m_Controls->m_LoadBvalsEdit->setText("-"); else m_Controls->m_LoadBvalsEdit->setText(QString(filename.c_str())); } void QmitkFiberfoxView::SetBvecsEdit() { // SELECT FOLDER DIALOG std::string filename; filename = QFileDialog::getOpenFileName(nullptr, "Select bvecs file", QString(filename.c_str())).toStdString(); if (filename.empty()) m_Controls->m_LoadBvecsEdit->setText("-"); else m_Controls->m_LoadBvecsEdit->setText(QString(filename.c_str())); } void QmitkFiberfoxView::SetOutputPath() { // SELECT FOLDER DIALOG std::string outputPath; outputPath = QFileDialog::getExistingDirectory(nullptr, "Save images to...", QString(outputPath.c_str())).toStdString(); if (outputPath.empty()) m_Controls->m_SavePathEdit->setText("-"); else { outputPath += "/"; m_Controls->m_SavePathEdit->setText(QString(outputPath.c_str())); } } void QmitkFiberfoxView::UpdateGui() { OnTlineChanged(); m_Controls->m_GeometryFrame->setEnabled(true); m_Controls->m_GeometryMessage->setVisible(false); m_Controls->m_DiffusionPropsMessage->setVisible(false); m_Controls->m_LoadGradientsFrame->setVisible(false); m_Controls->m_GenerateGradientsFrame->setVisible(false); if (m_Controls->m_UseBvalsBvecsBox->isChecked()) m_Controls->m_LoadGradientsFrame->setVisible(true); else m_Controls->m_GenerateGradientsFrame->setVisible(true); // Signal generation gui if (m_Controls->m_MaskComboBox->GetSelectedNode().IsNotNull() || m_Controls->m_TemplateComboBox->GetSelectedNode().IsNotNull()) { m_Controls->m_GeometryMessage->setVisible(true); m_Controls->m_GeometryFrame->setEnabled(false); } if ( m_Controls->m_TemplateComboBox->GetSelectedNode().IsNotNull() && mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(m_Controls->m_TemplateComboBox->GetSelectedNode()->GetData()) ) ) { m_Controls->m_DiffusionPropsMessage->setVisible(true); m_Controls->m_GeometryMessage->setVisible(true); m_Controls->m_GeometryFrame->setEnabled(false); m_Controls->m_LoadGradientsFrame->setVisible(false); m_Controls->m_GenerateGradientsFrame->setVisible(false); } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxViewControls.ui index c59bf0cffa..fe7ddca672 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberfox/src/internal/QmitkFiberfoxViewControls.ui @@ -1,2891 +1,2891 @@ QmitkFiberfoxViewControls 0 0 490 2775 Form QGroupBox { background-color: transparent; } Intra-axonal Compartment 6 6 6 6 Select signal model for intra-axonal compartment. Stick Model Zeppelin Model Tensor Model Prototype Signal QFrame::NoFrame QFrame::Raised 0 0 0 0 Volume Fraction: Optional! If no volume fraction map for this compartment is set, the corresponding volume fractions are calculated from the input fibers. QGroupBox { background-color: transparent; } Inter-axonal Compartment 6 6 6 6 Select signal model for intra-axonal compartment. -- Stick Model Zeppelin Model Tensor Model QFrame::NoFrame QFrame::Raised 0 0 0 0 Volume Fraction: Optional! If no volume fraction map for this compartment is set, the corresponding volume fractions are calculated from the input fibers. QGroupBox { background-color: transparent; } Image Settings 6 6 6 6 color: rgb(255, 0, 0); Using geometry of selected image! QFrame::NoFrame QFrame::Raised 0 0 0 0 6 Inversion time (in ms) for inversion recovery sequences. If 0, no inversion pulse is simulated. 0 999999999 1 0 <html><head/><body><p><span style=" font-style:italic;">T</span><span style=" font-style:italic; vertical-align:sub;">inhom</span> Relaxation: </p></body></html> false Relaxation time due to magnetic field inhomogeneities (T2', in milliseconds). 1 10000 1 50 <html><head/><body><p>Repetition Time <span style=" font-style:italic;">TR</span>: </p></body></html> false TE in milliseconds 1 999999999 1 100 Partial Fourier: false Partial fourier factor (0.5-1) 3 0.500000000000000 1.000000000000000 0.100000000000000 1.000000000000000 Output one image per compartment containing the corresponding volume fractions per voxel. Reverse Phase Encoding Direction false Dwell time (time to read one line in k-space) in ms. 4 100.000000000000000 0.100000000000000 1.000000000000000 Number of coil elements used for the acquisiton. 1 128 1 1 Fiber radius used to calculate volume fractions (in µm). Set to 0 for automatic radius estimation. 9999.000000000000000 Dwell Time: false Disable partial volume. Treat voxel content as fiber-only if at least one fiber is present. Disable Partial Volume Effects false Acquisition Type: Fiber Radius: Signal Scale: <html><head/><body><p>Number of Channels:</p></body></html> false TR in milliseconds 1 999999999 1 4000 Single Shot EPI Conventional Spin Echo Fast Spin Echo 1 100000 1 100 Constant Linear Exponential <html><head/><body><p><span style=" font-style:italic;">TE</span>, <span style=" font-style:italic;">T</span><span style=" font-style:italic; vertical-align:sub;">inhom</span> and <span style=" font-style:italic;">T2</span> will have no effect if unchecked.</p></body></html> Simulate Signal Relaxation true <html><head/><body><p>Echo Time <span style=" font-style:italic;">TE</span>: </p></body></html> false <html><head/><body><p>Coil Sensitivity:</p></body></html> false <html><head/><body><p>Inversion Time <span style=" font-style:italic;">TI</span>: </p></body></html> false Output phase image and volume fraction maps. Output Additional Images false <html><head/><body><p>Echo Train Length: </p></body></html> false Only relevant for Fast Spin Echo sequence (number of k-space lines acquired with one RF pulse) 1 999999999 1 8 QFrame::NoFrame QFrame::Raised 0 0 0 0 6 <html><head/><body><p>b-Value<span style=" font-style:italic;"> [s/mm</span><span style=" font-style:italic; vertical-align:super;">2</span><span style=" font-style:italic;">]</span>:</p></body></html> false b-value in s/mm² 0 10000 100 1000 Gradient Directions: Number of gradient directions distributed over the half sphere. 0 10000 1 30 Advanced Options color: rgb(255, 0, 0); Using gradients of selected DWI! QFrame::NoFrame QFrame::Raised 0 0 0 0 3 0.100000000000000 50.000000000000000 0.100000000000000 2.000000000000000 Image Spacing: 3 0.100000000000000 50.000000000000000 0.100000000000000 2.000000000000000 3 0.100000000000000 50.000000000000000 0.100000000000000 2.000000000000000 Image Dimensions: Fiber sampling factor which determines the accuracy of the calculated fiber and non-fiber volume fractions. 1 1000 1 20 Fiber sampling factor which determines the accuracy of the calculated fiber and non-fiber volume fractions. 1 1000 1 20 Fiber sampling factor which determines the accuracy of the calculated fiber and non-fiber volume fractions. 1 1000 1 3 Use bvals/bvecs files QFrame::NoFrame QFrame::Raised 0 0 0 0 ... ... - false Bvecs: false Bvals: false - false QGroupBox { background-color: transparent; } Extra-axonal Compartments 6 6 6 6 QFrame::NoFrame QFrame::Raised 0 0 0 0 Volume Fraction: Select signal model for extra-axonal compartment. Ball Model Astrosticks Model Dot Model Prototype Signal Qt::Horizontal QFrame::NoFrame QFrame::Raised 0 0 0 0 Volume Fraction: Optional! If no volume fraction map for this compartment is set, the corresponding volume fractions are calculated from the input fibers. Select signal model for extra-axonal compartment. -- Ball Model Astrosticks Model Dot Model Prototype Signal QGroupBox { background-color: transparent; } Noise and other Artifacts 6 6 6 6 Qt::Horizontal true QFrame::NoFrame QFrame::Raised 0 6 0 0 6 Toggle between random movement and linear movement. Randomize motion true QGroupBox { background-color: transparent; } Rotation 6 9 6 6 Degree: false x false Axis: false Maximum rotation around x-axis. 1 -360.000000000000000 360.000000000000000 1.000000000000000 0.000000000000000 Maximum rotation around z-axis. 1 -360.000000000000000 360.000000000000000 1.000000000000000 15.000000000000000 y false z false Maximum rotation around y-axis. 1 -360.000000000000000 360.000000000000000 1.000000000000000 0.000000000000000 QGroupBox { background-color: transparent; } Translation 6 6 6 Distance: false x false y false Axis: false z false Maximum translation along x-axis. 1 -1000.000000000000000 1000.000000000000000 1.000000000000000 0.000000000000000 Maximum translation along y-axis. 1 -1000.000000000000000 1000.000000000000000 1.000000000000000 0.000000000000000 Maximum translation along z-axis. 1 -1000.000000000000000 1000.000000000000000 1.000000000000000 0.000000000000000 QFrame::NoFrame QFrame::Raised 0 0 0 0 Motion volumes: Type in the volume indices that should be affected by motion (e.g. "0 3 7" whithout quotation marks). Leave blank for motion in all volumes. Type in "random" to randomly select volumes for motion. A list of negative numbers (e.g. -1 -2 -3) excludes volumes (e.g. 1 2 3) selects all remaining volumes. random QFrame::NoFrame QFrame::Raised 0 0 0 0 Num. Spikes: The number of randomly occurring signal spikes. 1 Spike amplitude relative to the largest signal amplitude of the corresponding k-space slice. 0.100000000000000 0.100000000000000 Scale: true QFrame::NoFrame QFrame::Raised 6 0 0 0 0 Shrink FOV (%): false Shrink FOV by this percentage. 1 0.000000000000000 90.000000000000000 0.100000000000000 40.000000000000000 Qt::Horizontal true QFrame::NoFrame QFrame::Raised 6 0 0 0 0 Signal Reduction (%): false Global signal in last simulated volume is specified percentage lower than in the first volume. 1 100.000000000000000 1.000000000000000 6.000000000000000 true QFrame::NoFrame QFrame::Raised 6 0 0 0 0 Frequency Map: false Select image specifying the frequency inhomogeneities (in Hz). true QFrame::NoFrame QFrame::Raised QFormLayout::AllNonFixedFieldsGrow 6 0 0 0 0 Gradient: false Eddy current induced magnetic field gradient (in mT/m). 5 1000.000000000000000 0.001000000000000 0.002000000000000 Qt::Horizontal Add Eddy Current Effects false Add Distortions false Add Spikes false Add Signal Drift false QFrame::NoFrame QFrame::Raised 0 0 0 0 Variance: Variance of selected noise distribution. 10 0.000000000000000 999999999.000000000000000 0.001000000000000 50.000000000000000 Distribution: Noise distribution Complex Gaussian Rician Qt::Horizontal Qt::Horizontal true QFrame::NoFrame QFrame::Raised 6 0 0 0 0 K-Space Line Offset: false A larger offset increases the inensity of the ghost image. 3 1.000000000000000 0.010000000000000 0.250000000000000 Add Motion Artifacts false Add N/2 Ghosts false Qt::Horizontal Add ringing artifacts occuring at strong edges in the image. Add Gibbs Ringing false Qt::Horizontal Add Noise false Qt::Horizontal Add Aliasing false If > 0, ringing is simulated by by setting the defined percentage of higher frequencies to 0 in k-space. Otherwise, the input to the k-space simulation is generated with twice the resolution and cropped during k-space simulation (much slower). 100 10 Qt::Vertical 20 40 QFrame::NoFrame QFrame::Raised 0 0 0 0 true <html><head/><body><p>Start DWI generation from selected fiber bundle.</p><p>If no fiber bundle but an existing diffusion weighted image is selected, the enabled artifacts are added to this image.</p><p>If neither a fiber bundle nor a diffusion weighted image is selected, a grayscale image containing a simple gradient is generated.</p></body></html> Save Parameters - - :/QmitkDiffusionImaging/general_icons/download.ico:/QmitkDiffusionImaging/general_icons/download.ico + + :/QmitkFiberfox/download.png:/QmitkFiberfox/download.png true <html><head/><body><p>Start DWI generation from selected fiber bundle.</p><p>If no fiber bundle but an existing diffusion weighted image is selected, the enabled artifacts are added to this image.</p><p>If neither a fiber bundle nor a diffusion weighted image is selected, a grayscale image containing a simple gradient is generated.</p></body></html> Load Parameters - - :/QmitkDiffusionImaging/general_icons/upload.ico:/QmitkDiffusionImaging/general_icons/upload.ico + + :/QmitkFiberfox/upload.png:/QmitkFiberfox/upload.png QFrame::NoFrame QFrame::Raised 0 0 0 0 true <html><head/><body><p>Start DWI generation from selected fiber bundle.</p><p>If no fiber bundle but an existing diffusion weighted image is selected, the enabled artifacts are added to this image.</p><p>If neither a fiber bundle nor a diffusion weighted image is selected, a grayscale image containing a simple gradient is generated.</p></body></html> Start Simulation - - :/QmitkDiffusionImaging/general_icons/right.ico:/QmitkDiffusionImaging/general_icons/right.ico + + :/QmitkFiberfox/right.png:/QmitkFiberfox/right.png QGroupBox { background-color: transparent; } Input Data 6 6 6 6 QFrame::NoFrame QFrame::Raised 0 0 0 0 0 - ... <html><head/><body><p>Select a binary image to define the area of signal generation. Outside of the mask image only noise will be actively generated.</p></body></html> QComboBox::AdjustToMinimumContentsLength Fiber Bundle: false Save path: false Tissue Mask: false <html><head/><body><p>Select a fiber bundle to generate the white matter signal from. You can either use the fiber definition tab to manually define an input fiber bundle or you can also use any existing bundle, e.g. yielded by a tractography algorithm.</p></body></html> QComboBox::AdjustToMinimumContentsLength Template Image: false <html><head/><body><p>The parameters for the simulation (e.g. spacing, size, diffuison-weighted gradients, b-value) are adopted from this image.</p></body></html> QComboBox::AdjustToMinimumContentsLength true Stop current simulation. Abort Simulation - - :/QmitkDiffusionImaging/general_icons/abort.ico:/QmitkDiffusionImaging/general_icons/abort.ico + + :/QmitkFiberfox/stop.png:/QmitkFiberfox/stop.png Courier 7 true QmitkDataStorageComboBox QComboBox
QmitkDataStorageComboBox.h
QmitkDataStorageComboBoxWithSelectNone QComboBox
QmitkDataStorageComboBoxWithSelectNone.h
QmitkTensorModelParametersWidget QWidget
QmitkTensorModelParametersWidget.h
1
QmitkStickModelParametersWidget QWidget
QmitkStickModelParametersWidget.h
1
QmitkZeppelinModelParametersWidget QWidget
QmitkZeppelinModelParametersWidget.h
1
QmitkBallModelParametersWidget QWidget
QmitkBallModelParametersWidget.h
1
QmitkAstrosticksModelParametersWidget QWidget
QmitkAstrosticksModelParametersWidget.h
1
QmitkDotModelParametersWidget QWidget
QmitkDotModelParametersWidget.h
1
QmitkPrototypeSignalParametersWidget QWidget
QmitkPrototypeSignalParametersWidget.h
1
m_FiberBundleComboBox m_MaskComboBox m_TemplateComboBox m_SavePathEdit m_OutputPathButton m_GenerateImageButton m_AbortSimulationButton m_SimulationStatusText m_LoadParametersButton m_SaveParametersButton m_SizeX m_SizeY m_SizeZ m_SpacingX m_SpacingY m_SpacingZ m_UseBvalsBvecsBox m_LoadBvalsEdit m_LoadBvalsButton m_LoadBvecsEdit m_LoadBvecsButton m_NumGradientsBox m_BvalueBox m_AdvancedOptionsBox_2 m_AcquisitionTypeBox m_SignalScaleBox m_NumCoilsBox m_CoilSensBox m_TEbox m_TRbox m_TIbox m_LineReadoutTimeBox m_PartialFourier m_T2starBox m_FiberRadius m_ReversePhaseBox m_RelaxationBox m_EnforcePureFiberVoxelsBox m_VolumeFractionsBox m_Compartment1Box m_Comp1VolumeFraction m_Compartment2Box m_Comp2VolumeFraction m_Compartment3Box m_Comp3VolumeFraction m_Compartment4Box m_Comp4VolumeFraction m_AddNoise m_NoiseDistributionBox m_NoiseLevel m_AddSpikes m_SpikeNumBox m_SpikeScaleBox m_AddGhosts m_kOffsetBox m_AddAliasing m_WrapBox m_AddDistortions m_FrequencyMapBox m_AddDrift m_DriftFactor m_AddMotion m_RandomMotion m_MotionVolumesBox m_MaxRotationBoxX m_MaxRotationBoxY m_MaxRotationBoxZ m_MaxTranslationBoxX m_MaxTranslationBoxY m_MaxTranslationBoxZ m_AddEddy m_EddyGradientStrength m_AddGibbsRinging m_ZeroRinging - +
diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/files.cmake b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/files.cmake index 0993574f5c..0f586c99c6 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/files.cmake +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/files.cmake @@ -1,63 +1,63 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkFiberQuantificationView.cpp QmitkFiberProcessingView.cpp QmitkFiberClusteringView.cpp QmitkFiberFitView.cpp QmitkTractometryView.cpp Perspectives/QmitkFiberProcessingPerspective.cpp ) set(UI_FILES src/internal/QmitkFiberQuantificationViewControls.ui src/internal/QmitkFiberProcessingViewControls.ui src/internal/QmitkFiberClusteringViewControls.ui src/internal/QmitkFiberFitViewControls.ui src/internal/QmitkTractometryViewControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkFiberQuantificationView.h src/internal/QmitkFiberProcessingView.h src/internal/QmitkFiberClusteringView.h src/internal/QmitkFiberFitView.h src/internal/QmitkTractometryView.h src/internal/Perspectives/QmitkFiberProcessingPerspective.h ) set(CACHED_RESOURCE_FILES plugin.xml resources/FiberBundleOperations.png resources/FiberQuantification.png resources/FiberClustering.png resources/FiberFit.png resources/Tractometry.png resources/circle.png resources/polygon.png ) set(QRC_FILES - resources/QmitkDiffusionImaging.qrc + resources/QmitkFiberprocessing.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/resources/QmitkDiffusionImaging.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/resources/QmitkFiberprocessing.qrc similarity index 73% rename from Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/resources/QmitkDiffusionImaging.qrc rename to Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/resources/QmitkFiberprocessing.qrc index 626629f87b..991f2d10e7 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/resources/QmitkDiffusionImaging.qrc +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/resources/QmitkFiberprocessing.qrc @@ -1,7 +1,7 @@ - + FiberBundle.png circle.png polygon.png diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberFitView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberFitView.cpp index 1ca7905f97..2374247305 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberFitView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberFitView.cpp @@ -1,265 +1,265 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "QmitkFiberFitView.h" #include #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkFiberFitView::VIEW_ID = "org.mitk.views.fiberfit"; using namespace mitk; QmitkFiberFitView::QmitkFiberFitView() : QmitkAbstractView() , m_Controls( nullptr ) { } // Destructor QmitkFiberFitView::~QmitkFiberFitView() { } void QmitkFiberFitView::CreateQtPartControl( QWidget *parent ) { // build up qt view, unless already done if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkFiberFitViewControls; m_Controls->setupUi( parent ); connect( m_Controls->m_StartButton, SIGNAL(clicked()), this, SLOT(StartFit()) ); connect( m_Controls->m_ImageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(DataSelectionChanged()) ); connect( m_Controls->m_TractBox, SIGNAL(currentIndexChanged(int)), this, SLOT(DataSelectionChanged()) ); mitk::TNodePredicateDataType::Pointer isFib = mitk::TNodePredicateDataType::New(); mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateDimension::Pointer is3D = mitk::NodePredicateDimension::New(3); m_Controls->m_TractBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_TractBox->SetPredicate(isFib); m_Controls->m_ImageBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_ImageBox->SetPredicate( mitk::NodePredicateOr::New( mitk::NodePredicateAnd::New(isImage, is3D), mitk::TNodePredicateDataType::New()) ); DataSelectionChanged(); } } void QmitkFiberFitView::DataSelectionChanged() { if (m_Controls->m_TractBox->GetSelectedNode().IsNull() || m_Controls->m_ImageBox->GetSelectedNode().IsNull()) m_Controls->m_StartButton->setEnabled(false); else m_Controls->m_StartButton->setEnabled(true); } void QmitkFiberFitView::SetFocus() { DataSelectionChanged(); } void QmitkFiberFitView::StartFit() { if (m_Controls->m_TractBox->GetSelectedNode().IsNull() || m_Controls->m_ImageBox->GetSelectedNode().IsNull()) return; - mitk::FiberBundle::Pointer input_tracts = dynamic_cast(m_Controls->m_TractBox->GetSelectedNode()->GetData()); + mitk::FiberBundle::Pointer input_tracts = dynamic_cast(m_Controls->m_TractBox->GetSelectedNode()->GetData())->GetDeepCopy(); mitk::DataNode::Pointer node = m_Controls->m_ImageBox->GetSelectedNode(); itk::FitFibersToImageFilter::Pointer fitter = itk::FitFibersToImageFilter::New(); mitk::Image::Pointer mitk_image = dynamic_cast(node->GetData()); mitk::PeakImage::Pointer mitk_peak_image = dynamic_cast(node->GetData()); if (mitk_peak_image.IsNotNull()) { typedef mitk::ImageToItk< mitk::PeakImage::ItkPeakImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(mitk_peak_image); caster->Update(); mitk::PeakImage::ItkPeakImageType::Pointer peak_image = caster->GetOutput(); fitter->SetPeakImage(peak_image); } else { if (mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(mitk_image)) { fitter->SetDiffImage(mitk::DiffusionPropertyHelper::GetItkVectorImage(mitk_image)); mitk::TensorModel<>* model = new mitk::TensorModel<>(); model->SetBvalue(1000); model->SetDiffusivity1(0.0010); model->SetDiffusivity2(0.00015); model->SetDiffusivity3(0.00015); model->SetGradientList(mitk::DiffusionPropertyHelper::GetGradientContainer(mitk_image)); fitter->SetSignalModel(model); } else { itk::FitFibersToImageFilter::DoubleImgType::Pointer scalar_image = itk::FitFibersToImageFilter::DoubleImgType::New(); mitk::CastToItkImage(mitk_image, scalar_image); fitter->SetScalarImage(scalar_image); } } if (m_Controls->m_ReguTypeBox->currentIndex()==0) fitter->SetRegularization(VnlCostFunction::REGU::VOXEL_VARIANCE); else if (m_Controls->m_ReguTypeBox->currentIndex()==1) fitter->SetRegularization(VnlCostFunction::REGU::VARIANCE); else if (m_Controls->m_ReguTypeBox->currentIndex()==2) fitter->SetRegularization(VnlCostFunction::REGU::MSM); else if (m_Controls->m_ReguTypeBox->currentIndex()==3) fitter->SetRegularization(VnlCostFunction::REGU::LASSO); else if (m_Controls->m_ReguTypeBox->currentIndex()==4) fitter->SetRegularization(VnlCostFunction::REGU::NONE); fitter->SetTractograms({input_tracts}); fitter->SetFitIndividualFibers(true); fitter->SetMaxIterations(20); fitter->SetVerbose(true); fitter->SetGradientTolerance(1e-5); fitter->SetLambda(m_Controls->m_ReguBox->value()); fitter->SetFilterOutliers(m_Controls->m_OutliersBox->isChecked()); fitter->Update(); mitk::FiberBundle::Pointer output_tracts = fitter->GetTractograms().at(0); output_tracts->ColorFibersByFiberWeights(false, true); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetData(output_tracts); new_node->SetName("Fitted"); this->GetDataStorage()->Add(new_node, node); m_Controls->m_TractBox->GetSelectedNode()->SetVisibility(false); if (m_Controls->m_ResidualsBox->isChecked() && mitk_peak_image.IsNotNull()) { { mitk::PeakImage::ItkPeakImageType::Pointer itk_image = fitter->GetFittedImage(); mitk::Image::Pointer mitk_image = dynamic_cast(PeakImage::New().GetPointer()); mitk::CastToMitkImage(itk_image, mitk_image); mitk_image->SetVolume(itk_image->GetBufferPointer()); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetData(mitk_image); new_node->SetName("Fitted"); this->GetDataStorage()->Add(new_node, node); } { mitk::PeakImage::ItkPeakImageType::Pointer itk_image = fitter->GetResidualImage(); mitk::Image::Pointer mitk_image = dynamic_cast(PeakImage::New().GetPointer()); mitk::CastToMitkImage(itk_image, mitk_image); mitk_image->SetVolume(itk_image->GetBufferPointer()); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetData(mitk_image); new_node->SetName("Residual"); this->GetDataStorage()->Add(new_node, node); } { mitk::PeakImage::ItkPeakImageType::Pointer itk_image = fitter->GetUnderexplainedImage(); mitk::Image::Pointer mitk_image = dynamic_cast(PeakImage::New().GetPointer()); mitk::CastToMitkImage(itk_image, mitk_image); mitk_image->SetVolume(itk_image->GetBufferPointer()); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetData(mitk_image); new_node->SetName("Underexplained"); this->GetDataStorage()->Add(new_node, node); } { mitk::PeakImage::ItkPeakImageType::Pointer itk_image = fitter->GetOverexplainedImage(); mitk::Image::Pointer mitk_image = dynamic_cast(PeakImage::New().GetPointer()); mitk::CastToMitkImage(itk_image, mitk_image); mitk_image->SetVolume(itk_image->GetBufferPointer()); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetData(mitk_image); new_node->SetName("Overexplained"); this->GetDataStorage()->Add(new_node, node); } } else if (m_Controls->m_ResidualsBox->isChecked() && mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(mitk_image)) { { mitk::Image::Pointer outImage = mitk::GrabItkImageMemory( fitter->GetFittedImageDiff().GetPointer() ); mitk::DiffusionPropertyHelper::CopyProperties(mitk_image, outImage, true); mitk::DiffusionPropertyHelper::InitializeImage( outImage ); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetData(outImage); new_node->SetName("Fitted"); this->GetDataStorage()->Add(new_node, node); } { mitk::Image::Pointer outImage = mitk::GrabItkImageMemory( fitter->GetResidualImageDiff().GetPointer() ); mitk::DiffusionPropertyHelper::CopyProperties(mitk_image, outImage, true); mitk::DiffusionPropertyHelper::InitializeImage( outImage ); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetData(outImage); new_node->SetName("Residual"); this->GetDataStorage()->Add(new_node, node); } } else if (m_Controls->m_ResidualsBox->isChecked()) { { mitk::Image::Pointer outImage = mitk::GrabItkImageMemory( fitter->GetFittedImageScalar().GetPointer() ); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetData(outImage); new_node->SetName("Fitted"); this->GetDataStorage()->Add(new_node, node); } { mitk::Image::Pointer outImage = mitk::GrabItkImageMemory( fitter->GetResidualImageScalar().GetPointer() ); mitk::DataNode::Pointer new_node = mitk::DataNode::New(); new_node->SetData(outImage); new_node->SetName("Residual"); this->GetDataStorage()->Add(new_node, node); } } } void QmitkFiberFitView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& ) { } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberFitViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberFitViewControls.ui index 521636a52e..2b50c5e107 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberFitViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberFitViewControls.ui @@ -1,224 +1,227 @@ - + QmitkFiberFitViewControls 0 0 484 574 Form QCommandLinkButton:disabled { border: none; } QGroupBox { background-color: transparent; } QFrame::NoFrame QFrame::Raised 0 0 0 0 6 - + false Output Residuals: - + Weight for regularization. + + 4 + 999999.000000000000000 0.100000000000000 0.100000000000000 Tractogram: false 0 0 200 16777215 11 - + Start - + λ: Image: - + false Suppress Outliers: Regularization: Voxel-wise Variance Variance Mean Squared Magnitude Lasso None Qt::Vertical 20 40 QmitkDataStorageComboBox QComboBox
QmitkDataStorageComboBox.h
- - -
\ No newline at end of file + + + diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingViewControls.ui index e56f21b27a..8708175130 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.fiberprocessing/src/internal/QmitkFiberProcessingViewControls.ui @@ -1,1732 +1,1731 @@ QmitkFiberProcessingViewControls 0 0 385 711 Form QCommandLinkButton:disabled { border: none; } QGroupBox { background-color: transparent; } 9 9 9 9 75 true 0 5 0 - -18 + 0 353 503 Fiber Extraction Extract a fiber subset from the selected fiber bundle using manually placed planar figures as waypoints or binary regions of interest. Qt::Vertical 20 40 QFrame::NoFrame QFrame::Raised 0 0 0 0 Min. overlap: Threshold: Min. num. fibers: Threshold on ROI image for positions to be considered as positive. 3 9999.000000000000000 0.100000000000000 0.500000000000000 0 0 Ending in ROI Not ending in ROI Passing ROI Not passing ROI Labelmap Both ends true Interpolate ROI true 1 999999999 Extract fibers: Minimum overlap of streamlines and ROI in terms of streamline length. Zero means that one streamline point inside the ROI is enough to be considered as "overlapping". 3 1.000000000000000 0.100000000000000 Labels: Label values seperated by whitespace. ALL false 0 0 200 16777215 11 Extract fibers passing through selected ROI or composite ROI. Select ROI and fiber bundle to execute. Extract 0 0 Extract using planar figures Extract using ROI image QFrame::NoFrame QFrame::Raised 0 0 0 0 6 Interactive Extraction 0 0 200 0 16777215 60 QFrame::NoFrame QFrame::Raised 0 0 0 0 30 30 Draw circular ROI. Select reference fiber bundle to execute. - - :/QmitkDiffusionImaging/circle.png:/QmitkDiffusionImaging/circle.png + + :/org.mitk.gui.qt.diffusionimaging.fiberprocessing/resources/circle.png:/org.mitk.gui.qt.diffusionimaging.fiberprocessing/resources/circle.png 32 32 false true Qt::Horizontal 40 20 30 30 Draw polygonal ROI. Select reference fiber bundle to execute. - - :/QmitkDiffusionImaging/polygon.png:/QmitkDiffusionImaging/polygon.png + + :/org.mitk.gui.qt.diffusionimaging.fiberprocessing/resources/polygon.png:/org.mitk.gui.qt.diffusionimaging.fiberprocessing/resources/polygon.png 32 32 true true 0 0 200 0 16777215 60 QFrame::NoFrame QFrame::Raised 0 0 0 0 false 60 16777215 Create NOT composition from selected ROI. NOT false 60 16777215 Create OR composition with selected ROIs. OR Qt::Horizontal 40 20 false 60 16777215 Create AND composition with selected ROIs. AND false 0 0 16777215 16777215 11 Generate a binary image containing all selected ROIs. Select at least one ROI (planar figure) and a reference fiber bundle or image. Generate ROI Image 0 - -34 + 0 353 456 Fiber Removal Remove fibers that satisfy certain criteria from the selected bundle. QFrame::NoFrame QFrame::Raised 0 0 0 0 0 X: Y: Z: Angle: Angular deviation threshold in degree 1 90.000000000000000 1.000000000000000 25.000000000000000 QFrame::NoFrame QFrame::Raised 0 0 0 0 0 Weight threshold: Only fibers with weight larger than this threshold are kept. 5 99999.000000000000000 0.100000000000000 false 0 0 200 16777215 11 Remove 0 0 Remove fibers in direction Remove fibers by length Remove fibers by curvature Remove fiber parts outside mask Remove fiber parts inside mask Remove fibers by weight Remove fibers by tract density QFrame::NoFrame QFrame::Raised 0 0 0 0 If unchecked, the fiber exceeding the threshold will be split in two instead of removed. Remove Fiber false QFrame::NoFrame QFrame::Raised 0 0 0 0 0 Max. Angular Deviation: Qt::Horizontal 40 20 Maximum angular deviation in degree 180.000000000000000 0.100000000000000 30.000000000000000 Distance: Distance in mm 1 999.000000000000000 1.000000000000000 10.000000000000000 Qt::Vertical 20 40 QFrame::NoFrame QFrame::Raised 0 0 0 0 Qt::Horizontal 40 20 Minimum fiber length in mm 0 999999999 20 Max. Length: Min. Length: Maximum fiber length in mm 0 999999999 300 QFrame::NoFrame QFrame::Raised 0 0 0 0 0 6 5 0.000000000000000 10.000000000000000 0.100000000000000 0.010000000000000 Density threshold: Min. overlap: Tracts have to spend at least this fraction of their length inside the specified density region. 3 1.000000000000000 0.100000000000000 0.500000000000000 0 0 367 408 Bundle Modification Modify the selected bundle with operations such as fiber resampling, FA coloring, etc. QFrame::NoFrame QFrame::Raised 0 0 0 0 0 6 Error threshold in mm: 999999999.000000000000000 0.100000000000000 0.100000000000000 QFrame::NoFrame QFrame::Raised 0 0 0 0 0 6 Sagittal Coronal Axial Select direction: QFrame::NoFrame QFrame::Raised 0 0 0 0 0 6 If checked, the image values are not only used to color the fibers but are also used as opaxity values. Values as opacity false Scalar map: The values used to color the fibers are min-max normalized. If not checked, the values should be between 0 and 1. Normalize values true 0 0 Resample fibers (spline) Resample fibers (linear) Compress fibers Color fibers by scalar map (e.g. FA) Mirror fibers Weight bundle Color fibers by curvature Color fibers by fiber weights Color fibers by length QFrame::NoFrame QFrame::Raised 0 0 0 0 0 6 0.010000000000000 999999999.000000000000000 0.100000000000000 1.000000000000000 Point distance in mm: Qt::Vertical 20 40 false 0 0 200 16777215 11 Execute QFrame::NoFrame QFrame::Raised 0 0 0 0 0 Weight: 7 999999999.000000000000000 0.100000000000000 1.000000000000000 0 0 367 182 Bundle Operations Join, subtract or copy bundles. false 0 0 200 16777215 11 Returns all fibers contained in bundle X that are not contained in bundle Y (not commutative!). Select at least two fiber bundles to execute. Substract Qt::Vertical 20 40 false 0 0 200 16777215 11 Merge selected fiber bundles. Select at least two fiber bundles to execute. Join false 0 0 200 16777215 11 Merge selected fiber bundles. Select at least two fiber bundles to execute. Copy Please Select Input Data 6 6 6 6 <html><head/><body><p><span style=" color:#ff0000;">mandatory</span></p></body></html> true <html><head/><body><p><span style=" color:#969696;">needed for extraction</span></p></body></html> true Input DTI Fiber Bundle: Binary seed ROI. If not specified, the whole image area is seeded. ROI: Qt::Vertical 20 40 QmitkDataStorageComboBox QComboBox
QmitkDataStorageComboBox.h
- - +
diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.ivim/resources/QmitkDiffusionImaging.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging.ivim/resources/QmitkDiffusionImaging.qrc deleted file mode 100644 index 489473dc74..0000000000 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.ivim/resources/QmitkDiffusionImaging.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - IVIM_48.png - - diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.preprocessing/src/internal/QmitkPreprocessingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.preprocessing/src/internal/QmitkPreprocessingView.cpp index e89d4822a2..228bcaa4ab 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.preprocessing/src/internal/QmitkPreprocessingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.preprocessing/src/internal/QmitkPreprocessingView.cpp @@ -1,1940 +1,1939 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ //#define MBILOG_ENABLE_DEBUG #include "QmitkPreprocessingView.h" #include "mitkDiffusionImagingConfigure.h" // qt includes #include // itk includes #include "itkTimeProbe.h" #include "itkB0ImageExtractionImageFilter.h" #include "itkB0ImageExtractionToSeparateImageFilter.h" #include "itkBrainMaskExtractionImageFilter.h" #include "itkCastImageFilter.h" #include "itkVectorContainer.h" #include #include #include #include #include #include // Multishell includes #include // Multishell Functors #include #include #include #include // mitk includes #include "QmitkDataStorageComboBox.h" #include "mitkProgressBar.h" #include "mitkStatusBar.h" #include "mitkNodePredicateDataType.h" #include "mitkProperties.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkLookupTable.h" #include "mitkLookupTableProperty.h" #include "mitkTransferFunction.h" #include "mitkTransferFunctionProperty.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkPreprocessingView::VIEW_ID = "org.mitk.views.diffusionpreprocessing"; #define DI_INFO MITK_INFO("DiffusionImaging") typedef float TTensorPixelType; QmitkPreprocessingView::QmitkPreprocessingView() : QmitkAbstractView(), m_Controls(nullptr) { } QmitkPreprocessingView::~QmitkPreprocessingView() { } void QmitkPreprocessingView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkPreprocessingViewControls; m_Controls->setupUi(parent); this->CreateConnections(); m_Controls->m_MeasurementFrameTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); m_Controls->m_MeasurementFrameTable->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); m_Controls->m_DirectionMatrixTable->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); m_Controls->m_DirectionMatrixTable->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); } } void QmitkPreprocessingView::SetFocus() { m_Controls->m_MirrorGradientToHalfSphereButton->setFocus(); } void QmitkPreprocessingView::CreateConnections() { if ( m_Controls ) { m_Controls->m_NormalizationMaskBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_SelctedImageComboBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_MergeDwiBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_AlignImageBox->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType::Pointer isMitkImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateDataType::Pointer isDti = mitk::NodePredicateDataType::New("TensorImage"); mitk::NodePredicateDataType::Pointer isOdf = mitk::NodePredicateDataType::New("OdfImage"); mitk::NodePredicateOr::Pointer isDiffusionImage = mitk::NodePredicateOr::New(isOdf, isDti); mitk::NodePredicateAnd::Pointer noDiffusionImage = mitk::NodePredicateAnd::New(isMitkImage, mitk::NodePredicateNot::New(isDiffusionImage)); mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer binaryNoDiffusionImage = mitk::NodePredicateAnd::New(noDiffusionImage, isBinaryPredicate); m_Controls->m_NormalizationMaskBox->SetPredicate(binaryNoDiffusionImage); m_Controls->m_SelctedImageComboBox->SetPredicate(isMitkImage); m_Controls->m_MergeDwiBox->SetPredicate(isMitkImage); m_Controls->m_AlignImageBox->SetPredicate(isMitkImage); m_Controls->m_ExtractBrainMask->setVisible(false); m_Controls->m_BrainMaskIterationsBox->setVisible(false); m_Controls->m_ResampleIntFrame->setVisible(false); connect( (QObject*)(m_Controls->m_ButtonAverageGradients), SIGNAL(clicked()), this, SLOT(AverageGradients()) ); connect( (QObject*)(m_Controls->m_ButtonExtractB0), SIGNAL(clicked()), this, SLOT(ExtractB0()) ); connect( (QObject*)(m_Controls->m_ReduceGradientsButton), SIGNAL(clicked()), this, SLOT(DoReduceGradientDirections()) ); connect( (QObject*)(m_Controls->m_ShowGradientsButton), SIGNAL(clicked()), this, SLOT(DoShowGradientDirections()) ); connect( (QObject*)(m_Controls->m_MirrorGradientToHalfSphereButton), SIGNAL(clicked()), this, SLOT(DoHalfSphereGradientDirections()) ); connect( (QObject*)(m_Controls->m_MergeDwisButton), SIGNAL(clicked()), this, SLOT(MergeDwis()) ); connect( (QObject*)(m_Controls->m_ProjectSignalButton), SIGNAL(clicked()), this, SLOT(DoProjectSignal()) ); connect( (QObject*)(m_Controls->m_B_ValueMap_Rounder_SpinBox), SIGNAL(valueChanged(int)), this, SLOT(UpdateDwiBValueMapRounder(int))); connect( (QObject*)(m_Controls->m_CreateLengthCorrectedDwi), SIGNAL(clicked()), this, SLOT(DoLengthCorrection()) ); connect( (QObject*)(m_Controls->m_NormalizeImageValuesButton), SIGNAL(clicked()), this, SLOT(DoDwiNormalization()) ); connect( (QObject*)(m_Controls->m_ResampleImageButton), SIGNAL(clicked()), this, SLOT(DoResampleImage()) ); connect( (QObject*)(m_Controls->m_ResampleTypeBox), SIGNAL(currentIndexChanged(int)), this, SLOT(DoUpdateInterpolationGui(int)) ); connect( (QObject*)(m_Controls->m_CropImageButton), SIGNAL(clicked()), this, SLOT(DoCropImage()) ); connect( (QObject*)(m_Controls->m_RemoveGradientButton), SIGNAL(clicked()), this, SLOT(DoRemoveGradient()) ); connect( (QObject*)(m_Controls->m_ExtractGradientButton), SIGNAL(clicked()), this, SLOT(DoExtractGradient()) ); connect( (QObject*)(m_Controls->m_FlipAxis), SIGNAL(clicked()), this, SLOT(DoFlipAxis()) ); connect( (QObject*)(m_Controls->m_FlipGradientsButton), SIGNAL(clicked()), this, SLOT(DoFlipGradientDirections()) ); connect( (QObject*)(m_Controls->m_ClearRotationButton), SIGNAL(clicked()), this, SLOT(DoClearRotationOfGradients()) ); connect( (QObject*)(m_Controls->m_ModifyHeader), SIGNAL(clicked()), this, SLOT(DoApplyHeader()) ); connect( (QObject*)(m_Controls->m_AlignImageButton), SIGNAL(clicked()), this, SLOT(DoAlignImages()) ); connect( (QObject*)(m_Controls->m_SelctedImageComboBox), SIGNAL(OnSelectionChanged(const mitk::DataNode*)), this, SLOT(OnImageSelectionChanged()) ); m_Controls->m_NormalizationMaskBox->SetZeroEntryText("--"); } } void QmitkPreprocessingView::DoAlignImages() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } mitk::DataNode::Pointer target_node = m_Controls->m_AlignImageBox->GetSelectedNode(); if (target_node.IsNull()) { return; } mitk::Image::Pointer target_image = dynamic_cast(target_node->GetData()); if ( target_image == nullptr ) { return; } image->SetOrigin(target_image->GetGeometry()->GetOrigin()); mitk::RenderingManager::GetInstance()->InitializeViews( image->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkPreprocessingView::DoFlipAxis() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(node) ); if (isDiffusionImage) { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); itk::FixedArray flipAxes; flipAxes[0] = m_Controls->m_FlipX->isChecked(); flipAxes[1] = m_Controls->m_FlipY->isChecked(); flipAxes[2] = m_Controls->m_FlipZ->isChecked(); itk::FlipImageFilter< ItkDwiType >::Pointer flipper = itk::FlipImageFilter< ItkDwiType >::New(); flipper->SetInput(itkVectorImagePointer); flipper->SetFlipAxes(flipAxes); flipper->Update(); auto oldGradients = PropHelper::GetGradientContainer(image); auto newGradients = GradProp::GradientDirectionsContainerType::New(); for (unsigned int i=0; iSize(); i++) { GradProp::GradientDirectionType g = oldGradients->GetElement(i); GradProp::GradientDirectionType newG = g; if (flipAxes[0]) { newG[0] *= -1; } if (flipAxes[1]) { newG[1] *= -1; } if (flipAxes[2]) { newG[2] *= -1; } newGradients->InsertElement(i, newG); } mitk::Image::Pointer newImage = mitk::GrabItkImageMemory( flipper->GetOutput() ); PropHelper::CopyProperties(image, newImage, true); PropHelper::SetGradientContainer(newImage, newGradients); PropHelper::InitializeImage(newImage); newImage->GetGeometry()->SetOrigin(image->GetGeometry()->GetOrigin()); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newImage ); QString name = node->GetName().c_str(); imageNode->SetName((name+"_flipped").toStdString().c_str()); GetDataStorage()->Add(imageNode, node); mitk::RenderingManager::GetInstance()->InitializeViews( imageNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if( image->GetPixelType().GetNumberOfComponents() == 1 ) { AccessFixedDimensionByItk(image, TemplatedFlipAxis,3); } else { QMessageBox::warning(nullptr,"Warning", QString("Operation not supported in multi-component images") ); } } template < typename TPixel, unsigned int VImageDimension > void QmitkPreprocessingView::TemplatedFlipAxis(itk::Image* itkImage) { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } itk::FixedArray flipAxes; flipAxes[0] = m_Controls->m_FlipX->isChecked(); flipAxes[1] = m_Controls->m_FlipY->isChecked(); flipAxes[2] = m_Controls->m_FlipZ->isChecked(); typename itk::FlipImageFilter< itk::Image >::Pointer flipper = itk::FlipImageFilter< itk::Image >::New(); flipper->SetInput(itkImage); flipper->SetFlipAxes(flipAxes); flipper->Update(); mitk::Image::Pointer newImage = mitk::GrabItkImageMemory( flipper->GetOutput() ); newImage->GetGeometry()->SetOrigin(image->GetGeometry()->GetOrigin()); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newImage ); QString name = node->GetName().c_str(); imageNode->SetName((name+"_flipped").toStdString().c_str()); GetDataStorage()->Add(imageNode, node); mitk::RenderingManager::GetInstance()->InitializeViews( imageNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkPreprocessingView::DoRemoveGradient() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( !isDiffusionImage ) { return; } std::vector< unsigned int > channelsToRemove; channelsToRemove.push_back(m_Controls->m_RemoveGradientBox->value()); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); itk::RemoveDwiChannelFilter< short >::Pointer filter = itk::RemoveDwiChannelFilter< short >::New(); filter->SetInput(itkVectorImagePointer); filter->SetChannelIndices(channelsToRemove); filter->SetDirections(PropHelper::GetGradientContainer(image)); filter->Update(); mitk::Image::Pointer newImage = mitk::GrabItkImageMemory( filter->GetOutput() ); PropHelper::CopyProperties(image, newImage, true); PropHelper::SetGradientContainer(newImage, filter->GetNewDirections()); PropHelper::InitializeImage( newImage ); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newImage ); QString name = node->GetName().c_str(); imageNode->SetName((name+"_removedgradients").toStdString().c_str()); GetDataStorage()->Add(imageNode, node); mitk::RenderingManager::GetInstance()->InitializeViews( imageNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkPreprocessingView::DoExtractGradient() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( !isDiffusionImage ) { return; } ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); unsigned int channel = m_Controls->m_ExtractGradientBox->value(); itk::ExtractDwiChannelFilter< short >::Pointer filter = itk::ExtractDwiChannelFilter< short >::New(); filter->SetInput( itkVectorImagePointer); filter->SetChannelIndex(channel); filter->Update(); mitk::Image::Pointer newImage = mitk::Image::New(); newImage->InitializeByItk( filter->GetOutput() ); newImage->SetImportChannel( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newImage ); QString name = node->GetName().c_str(); imageNode->SetName( (name+"_direction-"+QString::number(channel)).toStdString().c_str() ); GetDataStorage()->Add(imageNode, node); mitk::RenderingManager::GetInstance()->InitializeViews( imageNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } void QmitkPreprocessingView::DoCropImage() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( isDiffusionImage ) { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); ItkDwiType::SizeType lower; ItkDwiType::SizeType upper; lower[0] = m_Controls->m_XstartBox->value(); lower[1] = m_Controls->m_YstartBox->value(); lower[2] = m_Controls->m_ZstartBox->value(); upper[0] = m_Controls->m_XendBox->value(); upper[1] = m_Controls->m_YendBox->value(); upper[2] = m_Controls->m_ZendBox->value(); itk::CropImageFilter< ItkDwiType, ItkDwiType >::Pointer cropper = itk::CropImageFilter< ItkDwiType, ItkDwiType >::New(); cropper->SetLowerBoundaryCropSize(lower); cropper->SetUpperBoundaryCropSize(upper); cropper->SetInput( itkVectorImagePointer ); cropper->Update(); ItkDwiType::Pointer itkOutImage = cropper->GetOutput(); ItkDwiType::DirectionType dir = itkOutImage->GetDirection(); itk::Point origin = itkOutImage->GetOrigin(); itk::Point t; t[0] = lower[0]*itkOutImage->GetSpacing()[0]; t[1] = lower[1]*itkOutImage->GetSpacing()[1]; t[2] = lower[2]*itkOutImage->GetSpacing()[2]; t= dir*t; origin[0] += t[0]; origin[1] += t[1]; origin[2] += t[2]; itkOutImage->SetOrigin(origin); mitk::Image::Pointer newimage = mitk::GrabItkImageMemory( itkOutImage ); PropHelper::CopyProperties(image, newimage, false); PropHelper::InitializeImage( newimage ); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newimage ); QString name = node->GetName().c_str(); imageNode->SetName((name+"_cropped").toStdString().c_str()); GetDataStorage()->Add(imageNode, node); mitk::RenderingManager::GetInstance()->InitializeViews( imageNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); mitk::RenderingManager::GetInstance()->InitializeViews( imageNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if( image->GetPixelType().GetNumberOfComponents() ) { AccessFixedDimensionByItk(image, TemplatedCropImage,3); } else { QMessageBox::warning(nullptr,"Warning", QString("Operation not supported in multi-component images") ); } } template < typename TPixel, unsigned int VImageDimension > void QmitkPreprocessingView::TemplatedCropImage( itk::Image* itkImage) { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } ItkDwiType::SizeType lower; ItkDwiType::SizeType upper; lower[0] = m_Controls->m_XstartBox->value(); lower[1] = m_Controls->m_YstartBox->value(); lower[2] = m_Controls->m_ZstartBox->value(); upper[0] = m_Controls->m_XendBox->value(); upper[1] = m_Controls->m_YendBox->value(); upper[2] = m_Controls->m_ZendBox->value(); typedef itk::Image ImageType; typename itk::CropImageFilter< ImageType, ImageType >::Pointer cropper = itk::CropImageFilter< ImageType, ImageType >::New(); cropper->SetLowerBoundaryCropSize(lower); cropper->SetUpperBoundaryCropSize(upper); cropper->SetInput(itkImage); cropper->Update(); typename ImageType::Pointer itkOutImage = cropper->GetOutput(); typename ImageType::DirectionType dir = itkOutImage->GetDirection(); itk::Point origin = itkOutImage->GetOrigin(); itk::Point t; t[0] = lower[0]*itkOutImage->GetSpacing()[0]; t[1] = lower[1]*itkOutImage->GetSpacing()[1]; t[2] = lower[2]*itkOutImage->GetSpacing()[2]; t= dir*t; origin[0] += t[0]; origin[1] += t[1]; origin[2] += t[2]; itkOutImage->SetOrigin(origin); mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( itkOutImage.GetPointer() ); image->SetVolume( itkOutImage->GetBufferPointer() ); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( image ); QString name = node->GetName().c_str(); imageNode->SetName((name+"_cropped").toStdString().c_str()); GetDataStorage()->Add(imageNode, node); mitk::RenderingManager::GetInstance()->InitializeViews( imageNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkPreprocessingView::DoUpdateInterpolationGui(int i) { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } switch (i) { case 0: { m_Controls->m_ResampleIntFrame->setVisible(false); m_Controls->m_ResampleDoubleFrame->setVisible(true); break; } case 1: { m_Controls->m_ResampleIntFrame->setVisible(false); m_Controls->m_ResampleDoubleFrame->setVisible(true); mitk::BaseGeometry* geom = image->GetGeometry(); m_Controls->m_ResampleDoubleX->setValue(geom->GetSpacing()[0]); m_Controls->m_ResampleDoubleY->setValue(geom->GetSpacing()[1]); m_Controls->m_ResampleDoubleZ->setValue(geom->GetSpacing()[2]); break; } case 2: { m_Controls->m_ResampleIntFrame->setVisible(true); m_Controls->m_ResampleDoubleFrame->setVisible(false); mitk::BaseGeometry* geom = image->GetGeometry(); m_Controls->m_ResampleIntX->setValue(geom->GetExtent(0)); m_Controls->m_ResampleIntY->setValue(geom->GetExtent(1)); m_Controls->m_ResampleIntZ->setValue(geom->GetExtent(2)); break; } default: { m_Controls->m_ResampleIntFrame->setVisible(false); m_Controls->m_ResampleDoubleFrame->setVisible(true); } } } void QmitkPreprocessingView::DoExtractBrainMask() { } void QmitkPreprocessingView::DoResampleImage() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( isDiffusionImage ) { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); typedef itk::ResampleDwiImageFilter< short > ResampleFilter; ResampleFilter::Pointer resampler = ResampleFilter::New(); resampler->SetInput( itkVectorImagePointer ); switch (m_Controls->m_ResampleTypeBox->currentIndex()) { case 0: { itk::Vector< double, 3 > samplingFactor; samplingFactor[0] = m_Controls->m_ResampleDoubleX->value(); samplingFactor[1] = m_Controls->m_ResampleDoubleY->value(); samplingFactor[2] = m_Controls->m_ResampleDoubleZ->value(); resampler->SetSamplingFactor(samplingFactor); break; } case 1: { itk::Vector< double, 3 > newSpacing; newSpacing[0] = m_Controls->m_ResampleDoubleX->value(); newSpacing[1] = m_Controls->m_ResampleDoubleY->value(); newSpacing[2] = m_Controls->m_ResampleDoubleZ->value(); resampler->SetNewSpacing(newSpacing); break; } case 2: { itk::ImageRegion<3> newRegion; newRegion.SetSize(0, m_Controls->m_ResampleIntX->value()); newRegion.SetSize(1, m_Controls->m_ResampleIntY->value()); newRegion.SetSize(2, m_Controls->m_ResampleIntZ->value()); resampler->SetNewImageSize(newRegion); break; } default: { MITK_WARN << "Unknown resampling parameters!"; return; } } QString outAdd; switch (m_Controls->m_InterpolatorBox->currentIndex()) { case 0: { resampler->SetInterpolation(ResampleFilter::Interpolate_NearestNeighbour); outAdd = "NearestNeighbour"; break; } case 1: { resampler->SetInterpolation(ResampleFilter::Interpolate_Linear); outAdd = "Linear"; break; } case 2: { resampler->SetInterpolation(ResampleFilter::Interpolate_BSpline); outAdd = "BSpline"; break; } case 3: { resampler->SetInterpolation(ResampleFilter::Interpolate_WindowedSinc); outAdd = "WindowedSinc"; break; } default: { resampler->SetInterpolation(ResampleFilter::Interpolate_NearestNeighbour); outAdd = "NearestNeighbour"; } } resampler->Update(); mitk::Image::Pointer newImage = mitk::GrabItkImageMemory( resampler->GetOutput() ); PropHelper::CopyProperties(image, newImage, false); PropHelper::InitializeImage( newImage ); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newImage ); QString name = node->GetName().c_str(); imageNode->SetName((name+"_resampled_"+outAdd).toStdString().c_str()); imageNode->SetVisibility(false); GetDataStorage()->Add(imageNode, node); } else if( image->GetPixelType().GetNumberOfComponents() ) { AccessFixedDimensionByItk(image, TemplatedResampleImage,3); } else { QMessageBox::warning(nullptr,"Warning", QString("Operation not supported in multi-component images") ); } } template < typename TPixel, unsigned int VImageDimension > void QmitkPreprocessingView::TemplatedResampleImage( itk::Image* itkImage) { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } itk::Vector< double, 3 > newSpacing; itk::ImageRegion<3> newRegion; switch (m_Controls->m_ResampleTypeBox->currentIndex()) { case 0: { itk::Vector< double, 3 > sampling; sampling[0] = m_Controls->m_ResampleDoubleX->value(); sampling[1] = m_Controls->m_ResampleDoubleY->value(); sampling[2] = m_Controls->m_ResampleDoubleZ->value(); newSpacing = itkImage->GetSpacing(); newSpacing[0] /= sampling[0]; newSpacing[1] /= sampling[1]; newSpacing[2] /= sampling[2]; newRegion = itkImage->GetLargestPossibleRegion(); newRegion.SetSize(0, newRegion.GetSize(0)*sampling[0]); newRegion.SetSize(1, newRegion.GetSize(1)*sampling[1]); newRegion.SetSize(2, newRegion.GetSize(2)*sampling[2]); break; } case 1: { newSpacing[0] = m_Controls->m_ResampleDoubleX->value(); newSpacing[1] = m_Controls->m_ResampleDoubleY->value(); newSpacing[2] = m_Controls->m_ResampleDoubleZ->value(); itk::Vector< double, 3 > oldSpacing = itkImage->GetSpacing(); itk::Vector< double, 3 > sampling; sampling[0] = oldSpacing[0]/newSpacing[0]; sampling[1] = oldSpacing[1]/newSpacing[1]; sampling[2] = oldSpacing[2]/newSpacing[2]; newRegion = itkImage->GetLargestPossibleRegion(); newRegion.SetSize(0, newRegion.GetSize(0)*sampling[0]); newRegion.SetSize(1, newRegion.GetSize(1)*sampling[1]); newRegion.SetSize(2, newRegion.GetSize(2)*sampling[2]); break; } case 2: { newRegion.SetSize(0, m_Controls->m_ResampleIntX->value()); newRegion.SetSize(1, m_Controls->m_ResampleIntY->value()); newRegion.SetSize(2, m_Controls->m_ResampleIntZ->value()); itk::ImageRegion<3> oldRegion = itkImage->GetLargestPossibleRegion(); itk::Vector< double, 3 > sampling; sampling[0] = (double)newRegion.GetSize(0)/oldRegion.GetSize(0); sampling[1] = (double)newRegion.GetSize(1)/oldRegion.GetSize(1); sampling[2] = (double)newRegion.GetSize(2)/oldRegion.GetSize(2); newSpacing = itkImage->GetSpacing(); newSpacing[0] /= sampling[0]; newSpacing[1] /= sampling[1]; newSpacing[2] /= sampling[2]; break; } default: { MITK_WARN << "Unknown resampling parameters!"; return; } } itk::Point origin = itkImage->GetOrigin(); origin[0] -= itkImage->GetSpacing()[0]/2; origin[1] -= itkImage->GetSpacing()[1]/2; origin[2] -= itkImage->GetSpacing()[2]/2; origin[0] += newSpacing[0]/2; origin[1] += newSpacing[1]/2; origin[2] += newSpacing[2]/2; typedef itk::Image ImageType; typename ImageType::Pointer outImage = ImageType::New(); outImage->SetSpacing( newSpacing ); outImage->SetOrigin( origin ); outImage->SetDirection( itkImage->GetDirection() ); outImage->SetLargestPossibleRegion( newRegion ); outImage->SetBufferedRegion( newRegion ); outImage->SetRequestedRegion( newRegion ); outImage->Allocate(); typedef itk::ResampleImageFilter ResampleFilter; typename ResampleFilter::Pointer resampler = ResampleFilter::New(); resampler->SetInput(itkImage); resampler->SetOutputParametersFromImage(outImage); QString outAdd; switch (m_Controls->m_InterpolatorBox->currentIndex()) { case 0: { typename itk::NearestNeighborInterpolateImageFunction::Pointer interp = itk::NearestNeighborInterpolateImageFunction::New(); resampler->SetInterpolator(interp); outAdd = "NearestNeighbour"; break; } case 1: { typename itk::LinearInterpolateImageFunction::Pointer interp = itk::LinearInterpolateImageFunction::New(); resampler->SetInterpolator(interp); outAdd = "Linear"; break; } case 2: { typename itk::BSplineInterpolateImageFunction::Pointer interp = itk::BSplineInterpolateImageFunction::New(); resampler->SetInterpolator(interp); outAdd = "BSpline"; break; } case 3: { typename itk::WindowedSincInterpolateImageFunction::Pointer interp = itk::WindowedSincInterpolateImageFunction::New(); resampler->SetInterpolator(interp); outAdd = "WindowedSinc"; break; } default: { typename itk::NearestNeighborInterpolateImageFunction::Pointer interp = itk::NearestNeighborInterpolateImageFunction::New(); resampler->SetInterpolator(interp); outAdd = "NearestNeighbour"; } } resampler->Update(); mitk::Image::Pointer image = mitk::Image::New(); image->InitializeByItk( resampler->GetOutput() ); image->SetVolume( resampler->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( image ); QString name = node->GetName().c_str(); imageNode->SetName((name+"_resampled_"+outAdd).toStdString().c_str()); GetDataStorage()->Add(imageNode, node); } void QmitkPreprocessingView::DoApplyHeader() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); bool isDti = false; bool isOdf = false; bool isPeak = false; if (!isDiffusionImage) { if ( dynamic_cast(node->GetData()) ) isDti = true; else if ( dynamic_cast(node->GetData()) ) isOdf = true; else if ( dynamic_cast(node->GetData()) ) isPeak = true; } mitk::Image::Pointer newImage = image->Clone(); mitk::Vector3D spacing; spacing[0] = m_Controls->m_HeaderSpacingX->value(); spacing[1] = m_Controls->m_HeaderSpacingY->value(); spacing[2] = m_Controls->m_HeaderSpacingZ->value(); newImage->GetGeometry()->SetSpacing( spacing ); mitk::Point3D origin; origin[0] = m_Controls->m_HeaderOriginX->value(); origin[1] = m_Controls->m_HeaderOriginY->value(); origin[2] = m_Controls->m_HeaderOriginZ->value(); newImage->GetGeometry()->SetOrigin(origin); vtkSmartPointer< vtkMatrix4x4 > matrix = vtkSmartPointer< vtkMatrix4x4 >::New(); matrix->SetElement(0,3,newImage->GetGeometry()->GetOrigin()[0]); matrix->SetElement(1,3,newImage->GetGeometry()->GetOrigin()[1]); matrix->SetElement(2,3,newImage->GetGeometry()->GetOrigin()[2]); for (int r=0; r<3; r++) { for (int c=0; c<3; c++) { QTableWidgetItem* item = m_Controls->m_DirectionMatrixTable->item(r,c); if (!item) continue; matrix->SetElement(r,c,item->text().toDouble()); } } newImage->GetGeometry()->SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(matrix); if ( isDiffusionImage ) { vnl_matrix_fixed< double, 3, 3 > mf; for (int r=0; r<3; r++) { for (int c=0; c<3; c++) { QTableWidgetItem* item = m_Controls->m_MeasurementFrameTable->item(r,c); if (!item) continue; mf[r][c] = item->text().toDouble(); } } PropHelper::SetMeasurementFrame(newImage, mf); PropHelper::InitializeImage( newImage ); } mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); if (isOdf) { mitk::OdfImage::ItkOdfImageType::Pointer itk_img = mitk::OdfImage::ItkOdfImageType::New(); mitk::CastToItkImage(newImage, itk_img); mitk::Image::Pointer odfImage = dynamic_cast(mitk::OdfImage::New().GetPointer()); mitk::CastToMitkImage(itk_img, odfImage); odfImage->SetVolume(itk_img->GetBufferPointer()); imageNode->SetData( odfImage ); } else if (isDti) { mitk::TensorImage::ItkTensorImageType::Pointer itk_img = mitk::ImageToItkImage(newImage); mitk::Image::Pointer tensorImage = dynamic_cast(mitk::TensorImage::New().GetPointer()); mitk::CastToMitkImage(itk_img, tensorImage); tensorImage->SetVolume(itk_img->GetBufferPointer()); imageNode->SetData( tensorImage ); } else if (isPeak) { mitk::PeakImage::ItkPeakImageType::Pointer itk_img = mitk::ImageToItkImage(newImage); mitk::Image::Pointer peakImage = dynamic_cast(mitk::PeakImage::New().GetPointer()); mitk::CastToMitkImage(itk_img, peakImage); peakImage->SetVolume(itk_img->GetBufferPointer()); imageNode->SetData( peakImage ); } else imageNode->SetData( newImage ); QString name = node->GetName().c_str(); imageNode->SetName((name+"_newheader").toStdString().c_str()); GetDataStorage()->Add(imageNode, node); mitk::RenderingManager::GetInstance()->InitializeViews( imageNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkPreprocessingView::DoProjectSignal() { switch(m_Controls->m_ProjectionMethodBox->currentIndex()) { case 0: DoADCAverage(); break; case 1: DoAKCFit(); break; case 2: DoBiExpFit(); break; default: DoADCAverage(); } } void QmitkPreprocessingView::DoDwiNormalization() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( ! isDiffusionImage ) { return; } auto gradientContainer = PropHelper::GetGradientContainer(image); int b0Index = -1; for (unsigned int i=0; isize(); i++) { GradientDirectionType g = gradientContainer->GetElement(i); if (g.magnitude()<0.001) { b0Index = i; break; } } if (b0Index==-1) { return; } typedef itk::DwiNormilzationFilter FilterType; ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); FilterType::Pointer filter = FilterType::New(); filter->SetInput( itkVectorImagePointer ); filter->SetGradientDirections(PropHelper::GetGradientContainer(image)); filter->SetNewMean(m_Controls->m_NewMean->value()); filter->SetNewStdev(m_Controls->m_NewStdev->value()); UcharImageType::Pointer itkImage = nullptr; if (m_Controls->m_NormalizationMaskBox->GetSelectedNode().IsNotNull()) { itkImage = UcharImageType::New(); if ( dynamic_cast(m_Controls->m_NormalizationMaskBox->GetSelectedNode()->GetData()) != nullptr ) { mitk::CastToItkImage( dynamic_cast(m_Controls->m_NormalizationMaskBox->GetSelectedNode()->GetData()), itkImage ); } filter->SetMaskImage(itkImage); } filter->Update(); mitk::Image::Pointer newImage = mitk::GrabItkImageMemory( filter->GetOutput() ); PropHelper::CopyProperties(image, newImage, false); PropHelper::InitializeImage( newImage ); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newImage ); QString name = node->GetName().c_str(); imageNode->SetName((name+"_normalized").toStdString().c_str()); GetDataStorage()->Add(imageNode, node); } void QmitkPreprocessingView::DoLengthCorrection() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) return; mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image == nullptr ) return; if (!PropHelper::IsDiffusionWeightedImage(image)) return; typedef itk::DwiGradientLengthCorrectionFilter FilterType; ItkDwiType::Pointer itkVectorImagePointer = PropHelper::GetItkVectorImage(image); FilterType::Pointer filter = FilterType::New(); filter->SetRoundingValue( m_Controls->m_B_ValueMap_Rounder_SpinBox->value()); filter->SetReferenceBValue(PropHelper::GetReferenceBValue(image)); filter->SetReferenceGradientDirectionContainer(PropHelper::GetGradientContainer(image)); filter->Update(); mitk::Image::Pointer newImage = mitk::Image::New(); newImage->InitializeByItk( itkVectorImagePointer.GetPointer() ); newImage->SetImportVolume( itkVectorImagePointer->GetBufferPointer(), 0, 0, mitk::Image::CopyMemory); itkVectorImagePointer->GetPixelContainer()->ContainerManageMemoryOff(); PropHelper::CopyProperties(image, newImage, true); PropHelper::SetGradientContainer(newImage, filter->GetOutputGradientDirectionContainer()); PropHelper::SetReferenceBValue(newImage, filter->GetNewBValue()); PropHelper::InitializeImage( newImage ); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newImage ); QString name = node->GetName().c_str(); imageNode->SetName((name+"_rounded").toStdString().c_str()); GetDataStorage()->Add(imageNode, node); } void QmitkPreprocessingView::UpdateDwiBValueMapRounder(int i) { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(node) ); if ( ! isDiffusionImage ) { return; } UpdateBValueTableWidget(i); UpdateGradientDetails(); } void QmitkPreprocessingView:: CallMultishellToSingleShellFilter( itk::DWIVoxelFunctor * functor, mitk::Image::Pointer image, QString imageName, mitk::DataNode* parent ) { typedef itk::RadialMultishellToSingleshellImageFilter FilterType; // filter input parameter const mitk::BValueMapProperty::BValueMap& originalShellMap = PropHelper::GetBValueMap(image); ItkDwiType::Pointer itkVectorImagePointer = PropHelper::GetItkVectorImage(image); ItkDwiType* vectorImage = itkVectorImagePointer.GetPointer(); const GradProp::GradientDirectionsContainerType::Pointer gradientContainer = PropHelper::GetGradientContainer(image); const unsigned int& bValue = PropHelper::GetReferenceBValue(image); mitk::DataNode::Pointer imageNode = 0; // filter call FilterType::Pointer filter = FilterType::New(); filter->SetInput(vectorImage); filter->SetOriginalGradientDirections(gradientContainer); filter->SetOriginalBValueMap(originalShellMap); filter->SetOriginalBValue(bValue); filter->SetFunctor(functor); filter->Update(); // create new DWI image mitk::Image::Pointer outImage = mitk::GrabItkImageMemory( filter->GetOutput() ); PropHelper::CopyProperties(image, outImage, true); PropHelper::SetGradientContainer(outImage, filter->GetTargetGradientDirections()); PropHelper::SetReferenceBValue(outImage, m_Controls->m_targetBValueSpinBox->value()); PropHelper::InitializeImage( outImage ); imageNode = mitk::DataNode::New(); imageNode->SetData( outImage ); imageNode->SetName(imageName.toStdString().c_str()); GetDataStorage()->Add(imageNode, parent); } void QmitkPreprocessingView::DoBiExpFit() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( ! isDiffusionImage ) { return; } itk::BiExpFitFunctor::Pointer functor = itk::BiExpFitFunctor::New(); QString name(node->GetName().c_str()); const mitk::BValueMapProperty::BValueMap& originalShellMap = PropHelper::GetBValueMap(image); mitk::BValueMapProperty::BValueMap::const_iterator it = originalShellMap.begin(); ++it;/* skip b=0*/ unsigned int s = 0; /*shell index */ vnl_vector bValueList(originalShellMap.size()-1); while( it != originalShellMap.end() ) { bValueList.put(s++,(it++)->first); } const double targetBValue = m_Controls->m_targetBValueSpinBox->value(); functor->setListOfBValues(bValueList); functor->setTargetBValue(targetBValue); CallMultishellToSingleShellFilter(functor,image,name + "_BiExp", node); } void QmitkPreprocessingView::DoAKCFit() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( ! isDiffusionImage ) { return; } itk::KurtosisFitFunctor::Pointer functor = itk::KurtosisFitFunctor::New(); QString name(node->GetName().c_str()); const mitk::BValueMapProperty::BValueMap& originalShellMap = PropHelper::GetBValueMap(image); mitk::BValueMapProperty::BValueMap::const_iterator it = originalShellMap.begin(); ++it;/* skip b=0*/ unsigned int s = 0; /*shell index */ vnl_vector bValueList(originalShellMap.size()-1); while(it != originalShellMap.end()) bValueList.put(s++,(it++)->first); const double targetBValue = m_Controls->m_targetBValueSpinBox->value(); functor->setListOfBValues(bValueList); functor->setTargetBValue(targetBValue); CallMultishellToSingleShellFilter(functor,image,name + "_AKC", node); } void QmitkPreprocessingView::DoADCFit() { // later } void QmitkPreprocessingView::DoADCAverage() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( ! isDiffusionImage ) return; itk::ADCAverageFunctor::Pointer functor = itk::ADCAverageFunctor::New(); QString name(node->GetName().c_str()); const mitk::BValueMapProperty::BValueMap &originalShellMap = PropHelper::GetBValueMap(image); mitk::BValueMapProperty::BValueMap::const_iterator it = originalShellMap.begin(); ++it;/* skip b=0*/ unsigned int s = 0; /*shell index */ vnl_vector bValueList(originalShellMap.size()-1); while(it != originalShellMap.end()) bValueList.put(s++,(it++)->first); const double targetBValue = m_Controls->m_targetBValueSpinBox->value(); functor->setListOfBValues(bValueList); functor->setTargetBValue(targetBValue); CallMultishellToSingleShellFilter(functor,image,name + "_ADC", node); } void QmitkPreprocessingView::CleanBValueTableWidget() { m_Controls->m_B_ValueMap_TableWidget->clear(); m_Controls->m_B_ValueMap_TableWidget->setRowCount(1); QStringList headerList; headerList << "b-Value" << "Number of gradients"; m_Controls->m_B_ValueMap_TableWidget->setHorizontalHeaderLabels(headerList); m_Controls->m_B_ValueMap_TableWidget->setItem(0,0,new QTableWidgetItem("-")); m_Controls->m_B_ValueMap_TableWidget->setItem(0,1,new QTableWidgetItem("-")); } void QmitkPreprocessingView::UpdateGradientDetails() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage(false); isDiffusionImage = PropHelper::IsDiffusionWeightedImage(image); if ( ! isDiffusionImage ) { m_Controls->m_WorkingGradientsText->clear(); m_Controls->m_OriginalGradientsText->clear(); return; } auto gradientContainer = PropHelper::GetGradientContainer(image); QString text = ""; for (unsigned int j=0; jSize(); j++) { auto g = gradientContainer->at(j); g = g.normalize(); text += QString::number(g[0]); if (jSize()-1) text += " "; else text += "\n"; } for (unsigned int j=0; jSize(); j++) { auto g = gradientContainer->at(j); g = g.normalize(); text += QString::number(g[1]); if (jSize()-1) text += " "; else text += "\n"; } for (unsigned int j=0; jSize(); j++) { auto g = gradientContainer->at(j); g = g.normalize(); text += QString::number(g[2]); if (jSize()-1) text += " "; else text += "\n"; } m_Controls->m_WorkingGradientsText->setPlainText(text); gradientContainer = PropHelper::GetOriginalGradientContainer(image); text = ""; for (unsigned int j=0; jSize(); j++) { auto g = gradientContainer->at(j); g = g.normalize(); text += QString::number(g[0]); if (jSize()-1) text += " "; else text += "\n"; } for (unsigned int j=0; jSize(); j++) { auto g = gradientContainer->at(j); g = g.normalize(); text += QString::number(g[1]); if (jSize()-1) text += " "; else text += "\n"; } for (unsigned int j=0; jSize(); j++) { auto g = gradientContainer->at(j); g = g.normalize(); text += QString::number(g[2]); if (jSize()-1) text += " "; else text += "\n"; } m_Controls->m_OriginalGradientsText->setPlainText(text); } void QmitkPreprocessingView::UpdateBValueTableWidget(int) { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { CleanBValueTableWidget(); return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage(false); isDiffusionImage = PropHelper::IsDiffusionWeightedImage(image); if ( ! isDiffusionImage ) { CleanBValueTableWidget(); } else { typedef mitk::BValueMapProperty::BValueMap BValueMap; typedef mitk::BValueMapProperty::BValueMap::iterator BValueMapIterator; BValueMapIterator it; BValueMap roundedBValueMap = PropHelper::GetBValueMap(image); m_Controls->m_B_ValueMap_TableWidget->clear(); m_Controls->m_B_ValueMap_TableWidget->setRowCount(roundedBValueMap.size() ); QStringList headerList; headerList << "b-Value" << "Number of gradients"; m_Controls->m_B_ValueMap_TableWidget->setHorizontalHeaderLabels(headerList); int i = 0 ; for(it = roundedBValueMap.begin() ;it != roundedBValueMap.end(); it++) { m_Controls->m_B_ValueMap_TableWidget->setItem(i,0,new QTableWidgetItem(QString::number(it->first))); QTableWidgetItem* item = m_Controls->m_B_ValueMap_TableWidget->item(i,0); item->setFlags(item->flags() & ~Qt::ItemIsEditable); m_Controls->m_B_ValueMap_TableWidget->setItem(i,1,new QTableWidgetItem(QString::number(it->second.size()))); i++; } } } void QmitkPreprocessingView::OnSelectionChanged(berry::IWorkbenchPart::Pointer , const QList& nodes) { (void) nodes; this->OnImageSelectionChanged(); } void QmitkPreprocessingView::OnImageSelectionChanged() { for (int r=0; r<3; r++) for (int c=0; c<3; c++) { { QTableWidgetItem* item = m_Controls->m_MeasurementFrameTable->item( r, c ); delete item; item = new QTableWidgetItem(); m_Controls->m_MeasurementFrameTable->setItem( r, c, item ); } { QTableWidgetItem* item = m_Controls->m_DirectionMatrixTable->item( r, c ); delete item; item = new QTableWidgetItem(); m_Controls->m_DirectionMatrixTable->setItem( r, c, item ); } } bool foundImageVolume = true; mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } bool foundDwiVolume( PropHelper::IsDiffusionWeightedImage( node ) ); mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool multiComponentVolume = (image->GetPixelType().GetNumberOfComponents() > 1); bool threeDplusTVolume = (image->GetTimeSteps() > 1); m_Controls->m_ButtonAverageGradients->setEnabled(foundDwiVolume); m_Controls->m_ButtonExtractB0->setEnabled(foundDwiVolume); m_Controls->m_CheckExtractAll->setEnabled(foundDwiVolume); m_Controls->m_MeasurementFrameTable->setEnabled(foundDwiVolume); m_Controls->m_ReduceGradientsButton->setEnabled(foundDwiVolume); m_Controls->m_ShowGradientsButton->setEnabled(foundDwiVolume); m_Controls->m_MirrorGradientToHalfSphereButton->setEnabled(foundDwiVolume); m_Controls->m_MergeDwisButton->setEnabled(foundDwiVolume); m_Controls->m_B_ValueMap_Rounder_SpinBox->setEnabled(foundDwiVolume); m_Controls->m_ProjectSignalButton->setEnabled(foundDwiVolume); m_Controls->m_CreateLengthCorrectedDwi->setEnabled(foundDwiVolume); m_Controls->m_targetBValueSpinBox->setEnabled(foundDwiVolume); m_Controls->m_NormalizeImageValuesButton->setEnabled(foundDwiVolume); m_Controls->m_RemoveGradientButton->setEnabled(foundDwiVolume); m_Controls->m_ExtractGradientButton->setEnabled(foundDwiVolume); m_Controls->m_FlipGradientsButton->setEnabled(foundDwiVolume); m_Controls->m_ClearRotationButton->setEnabled(foundDwiVolume); m_Controls->m_DirectionMatrixTable->setEnabled(foundImageVolume); m_Controls->m_ModifyHeader->setEnabled(foundImageVolume); m_Controls->m_AlignImageBox->setEnabled(foundImageVolume); // we do not support multi-component and 3D+t images for certain operations bool foundSingleImageVolume = foundDwiVolume || (foundImageVolume && !(multiComponentVolume || threeDplusTVolume)); m_Controls->m_FlipAxis->setEnabled(foundSingleImageVolume); m_Controls->m_CropImageButton->setEnabled(foundSingleImageVolume); m_Controls->m_ExtractBrainMask->setEnabled(foundSingleImageVolume); m_Controls->m_ResampleImageButton->setEnabled(foundSingleImageVolume); // reset sampling frame to 1 and update all ealted components m_Controls->m_B_ValueMap_Rounder_SpinBox->setValue(1); UpdateBValueTableWidget(m_Controls->m_B_ValueMap_Rounder_SpinBox->value()); DoUpdateInterpolationGui(m_Controls->m_ResampleTypeBox->currentIndex()); UpdateGradientDetails(); m_Controls->m_HeaderSpacingX->setValue(image->GetGeometry()->GetSpacing()[0]); m_Controls->m_HeaderSpacingY->setValue(image->GetGeometry()->GetSpacing()[1]); m_Controls->m_HeaderSpacingZ->setValue(image->GetGeometry()->GetSpacing()[2]); m_Controls->m_HeaderOriginX->setValue(image->GetGeometry()->GetOrigin()[0]); m_Controls->m_HeaderOriginY->setValue(image->GetGeometry()->GetOrigin()[1]); m_Controls->m_HeaderOriginZ->setValue(image->GetGeometry()->GetOrigin()[2]); m_Controls->m_XstartBox->setMaximum(image->GetGeometry()->GetExtent(0)-1); m_Controls->m_YstartBox->setMaximum(image->GetGeometry()->GetExtent(1)-1); m_Controls->m_ZstartBox->setMaximum(image->GetGeometry()->GetExtent(2)-1); m_Controls->m_XendBox->setMaximum(image->GetGeometry()->GetExtent(0)-1); m_Controls->m_YendBox->setMaximum(image->GetGeometry()->GetExtent(1)-1); m_Controls->m_ZendBox->setMaximum(image->GetGeometry()->GetExtent(2)-1); for (int r=0; r<3; r++) for (int c=0; c<3; c++) { // Direction matrix QTableWidgetItem* item = m_Controls->m_DirectionMatrixTable->item( r, c ); delete item; item = new QTableWidgetItem(); item->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter); double val = image->GetGeometry()->GetVtkMatrix()->GetElement(r,c)/image->GetGeometry()->GetSpacing()[c]; item->setText(QString::number(val)); m_Controls->m_DirectionMatrixTable->setItem( r, c, item ); } if (foundDwiVolume) { m_Controls->m_InputData->setTitle("Input Data"); vnl_matrix_fixed< double, 3, 3 > mf = PropHelper::GetMeasurementFrame(image); for (int r=0; r<3; r++) for (int c=0; c<3; c++) { // Measurement frame QTableWidgetItem* item = m_Controls->m_MeasurementFrameTable->item( r, c ); delete item; item = new QTableWidgetItem(); item->setTextAlignment(Qt::AlignCenter | Qt::AlignVCenter); item->setText(QString::number(mf.get(r,c))); m_Controls->m_MeasurementFrameTable->setItem( r, c, item ); } //calculate target bValue for MultishellToSingleShellfilter const mitk::BValueMapProperty::BValueMap & bValMap = PropHelper::GetBValueMap(image); mitk::BValueMapProperty::BValueMap::const_iterator it = bValMap.begin(); unsigned int targetBVal = 0; while(it != bValMap.end()) { targetBVal += (it++)->first; } targetBVal /= (float)bValMap.size()-1; m_Controls->m_targetBValueSpinBox->setValue(targetBVal); m_Controls->m_RemoveGradientBox->setMaximum(PropHelper::GetGradientContainer(image)->Size()-1); m_Controls->m_ExtractGradientBox->setMaximum(PropHelper::GetGradientContainer(image)->Size()-1); } } void QmitkPreprocessingView::Activated() { } void QmitkPreprocessingView::Deactivated() { OnImageSelectionChanged(); } void QmitkPreprocessingView::Visible() { OnImageSelectionChanged(); } void QmitkPreprocessingView::Hidden() { } void QmitkPreprocessingView::DoClearRotationOfGradients() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) return; mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image == nullptr) return; if(!PropHelper::IsDiffusionWeightedImage(image)) return; mitk::Image::Pointer newDwi = image->Clone(); - PropHelper::InitializeImage( newDwi ); - PropHelper::ClearMeasurementFrameAndRotationMatrixFromGradients(newDwi); + PropHelper::SetApplyMatrixToGradients(newDwi, false); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newDwi ); QString name = node->GetName().c_str(); imageNode->SetName( (name+"_OriginalGradients").toStdString().c_str() ); GetDataStorage()->Add( imageNode, node ); } void QmitkPreprocessingView::DoFlipGradientDirections() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } mitk::Image::Pointer newDwi = image->Clone(); auto gradientContainer = PropHelper::GetGradientContainer(newDwi); for (unsigned int j=0; jSize(); j++) { if (m_Controls->m_FlipGradBoxX->isChecked()) { gradientContainer->at(j)[0] *= -1; } if (m_Controls->m_FlipGradBoxY->isChecked()) { gradientContainer->at(j)[1] *= -1; } if (m_Controls->m_FlipGradBoxZ->isChecked()) { gradientContainer->at(j)[2] *= -1; } } PropHelper::CopyProperties(image, newDwi, true); PropHelper::SetGradientContainer(newDwi, gradientContainer); PropHelper::InitializeImage( newDwi ); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newDwi ); QString name = node->GetName().c_str(); imageNode->SetName( (name+"_GradientFlip").toStdString().c_str() ); GetDataStorage()->Add( imageNode, node ); } void QmitkPreprocessingView::DoHalfSphereGradientDirections() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } mitk::Image::Pointer newDwi = image->Clone(); auto gradientContainer = PropHelper::GetGradientContainer(newDwi); for (unsigned int j=0; jSize(); j++) { if (gradientContainer->at(j)[0]<0) { gradientContainer->at(j) = -gradientContainer->at(j); } } PropHelper::CopyProperties(image, newDwi, true); PropHelper::SetGradientContainer(newDwi, gradientContainer); PropHelper::InitializeImage( newDwi ); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newDwi ); QString name = node->GetName().c_str(); imageNode->SetName( (name+"_halfsphere").toStdString().c_str() ); GetDataStorage()->Add( imageNode, node ); } void QmitkPreprocessingView::DoShowGradientDirections() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( !isDiffusionImage ) { return; } int maxIndex = 0; unsigned int maxSize = image->GetDimension(0); if (maxSizeGetDimension(1)) { maxSize = image->GetDimension(1); maxIndex = 1; } if (maxSizeGetDimension(2)) { maxSize = image->GetDimension(2); maxIndex = 2; } mitk::Point3D origin = image->GetGeometry()->GetOrigin(); mitk::PointSet::Pointer originSet = mitk::PointSet::New(); typedef mitk::BValueMapProperty::BValueMap BValueMap; typedef mitk::BValueMapProperty::BValueMap::iterator BValueMapIterator; BValueMap bValMap = PropHelper::GetBValueMap(image); auto gradientContainer = PropHelper::GetGradientContainer(image); mitk::BaseGeometry::Pointer geometry = image->GetGeometry(); int shellCount = 1; for(BValueMapIterator it = bValMap.begin(); it!=bValMap.end(); ++it) { mitk::PointSet::Pointer pointset = mitk::PointSet::New(); for (unsigned int j=0; jsecond.size(); j++) { mitk::Point3D ip; vnl_vector_fixed< double, 3 > v = gradientContainer->at(it->second[j]); if (v.magnitude()>mitk::eps) { ip[0] = v[0]*maxSize*geometry->GetSpacing()[maxIndex]/2 + origin[0]-0.5*geometry->GetSpacing()[0] + geometry->GetSpacing()[0]*image->GetDimension(0)/2; ip[1] = v[1]*maxSize*geometry->GetSpacing()[maxIndex]/2 + origin[1]-0.5*geometry->GetSpacing()[1] + geometry->GetSpacing()[1]*image->GetDimension(1)/2; ip[2] = v[2]*maxSize*geometry->GetSpacing()[maxIndex]/2 + origin[2]-0.5*geometry->GetSpacing()[2] + geometry->GetSpacing()[2]*image->GetDimension(2)/2; pointset->InsertPoint(j, ip); } else if (originSet->IsEmpty()) { ip[0] = v[0]*maxSize*geometry->GetSpacing()[maxIndex]/2 + origin[0]-0.5*geometry->GetSpacing()[0] + geometry->GetSpacing()[0]*image->GetDimension(0)/2; ip[1] = v[1]*maxSize*geometry->GetSpacing()[maxIndex]/2 + origin[1]-0.5*geometry->GetSpacing()[1] + geometry->GetSpacing()[1]*image->GetDimension(1)/2; ip[2] = v[2]*maxSize*geometry->GetSpacing()[maxIndex]/2 + origin[2]-0.5*geometry->GetSpacing()[2] + geometry->GetSpacing()[2]*image->GetDimension(2)/2; originSet->InsertPoint(j, ip); } } if ( it->first < mitk::eps ) { continue; } // add shell to datastorage mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetData(pointset); QString name = node->GetName().c_str(); name += "_Shell_"; name += QString::number( it->first ); newNode->SetName( name.toStdString().c_str() ); newNode->SetProperty( "pointsize", mitk::FloatProperty::New((float)maxSize / 50) ); int b0 = shellCount % 2; int b1 = 0; int b2 = 0; if (shellCount>4) { b2 = 1; } if (shellCount%4 >= 2) { b1 = 1; } newNode->SetProperty("color", mitk::ColorProperty::New( b2, b1, b0 )); GetDataStorage()->Add( newNode, node ); shellCount++; } // add origin to datastorage mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetData(originSet); QString name = node->GetName().c_str(); name += "_Origin"; newNode->SetName(name.toStdString().c_str()); newNode->SetProperty("pointsize", mitk::FloatProperty::New((float)maxSize/50)); newNode->SetProperty("color", mitk::ColorProperty::New(1,1,1)); GetDataStorage()->Add(newNode, node); } void QmitkPreprocessingView::DoReduceGradientDirections() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( !isDiffusionImage ) { return; } typedef itk::ElectrostaticRepulsionDiffusionGradientReductionFilter FilterType; typedef mitk::BValueMapProperty::BValueMap BValueMap; // GetShellSelection from GUI BValueMap shellSlectionMap; BValueMap originalShellMap = PropHelper::GetBValueMap(image); std::vector newNumGradientDirections; int shellCounter = 0; QString name = node->GetName().c_str(); for (int i=0; im_B_ValueMap_TableWidget->rowCount(); i++) { double BValue = m_Controls->m_B_ValueMap_TableWidget->item(i,0)->text().toDouble(); shellSlectionMap[BValue] = originalShellMap[BValue]; unsigned int num = m_Controls->m_B_ValueMap_TableWidget->item(i,1)->text().toUInt(); newNumGradientDirections.push_back(num); name += "_"; name += QString::number(num); shellCounter++; } if (newNumGradientDirections.empty()) { return; } auto gradientContainer = PropHelper::GetGradientContainer(image); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); FilterType::Pointer filter = FilterType::New(); filter->SetInput( itkVectorImagePointer ); filter->SetOriginalGradientDirections(gradientContainer); filter->SetNumGradientDirections(newNumGradientDirections); filter->SetOriginalBValueMap(originalShellMap); filter->SetShellSelectionBValueMap(shellSlectionMap); filter->SetUseFirstN(m_Controls->m_KeepFirstNBox->isChecked()); filter->Update(); mitk::Image::Pointer newImage = mitk::GrabItkImageMemory( filter->GetOutput() ); PropHelper::CopyProperties(image, newImage, true); PropHelper::SetGradientContainer(newImage, filter->GetGradientDirections()); PropHelper::InitializeImage(newImage); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newImage ); imageNode->SetName(name.toStdString().c_str()); GetDataStorage()->Add(imageNode, node); // update the b-value widget to remove the modified number of gradients used for extraction this->CleanBValueTableWidget(); this->UpdateBValueTableWidget(0); this->UpdateGradientDetails(); } void QmitkPreprocessingView::MergeDwis() { typedef GradProp::GradientDirectionsContainerType GradientContainerType; mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( !isDiffusionImage ) { return; } mitk::DataNode::Pointer node2 = m_Controls->m_MergeDwiBox->GetSelectedNode(); if (node2.IsNull()) { return; } mitk::Image::Pointer image2 = dynamic_cast(node2->GetData()); if ( image2 == nullptr ) { return; } typedef itk::VectorImage DwiImageType; typedef std::vector< DwiImageType::Pointer > DwiImageContainerType; typedef std::vector< GradientContainerType::Pointer > GradientListContainerType; DwiImageContainerType imageContainer; GradientListContainerType gradientListContainer; std::vector< double > bValueContainer; QString name = ""; { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); imageContainer.push_back( itkVectorImagePointer ); gradientListContainer.push_back(PropHelper::GetGradientContainer(image)); bValueContainer.push_back(PropHelper::GetReferenceBValue(image)); name += node->GetName().c_str(); } { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image2, itkVectorImagePointer); imageContainer.push_back( itkVectorImagePointer ); gradientListContainer.push_back(PropHelper::GetGradientContainer(image2)); bValueContainer.push_back(PropHelper::GetReferenceBValue(image2)); name += "+"; name += node2->GetName().c_str(); } typedef itk::MergeDiffusionImagesFilter FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetImageVolumes(imageContainer); filter->SetGradientLists(gradientListContainer); filter->SetBValues(bValueContainer); filter->Update(); vnl_matrix_fixed< double, 3, 3 > mf; mf.set_identity(); mitk::Image::Pointer newImage = mitk::GrabItkImageMemory( filter->GetOutput() ); PropHelper::SetGradientContainer(newImage, filter->GetOutputGradients()); PropHelper::SetMeasurementFrame(newImage, mf); PropHelper::SetReferenceBValue(newImage, filter->GetB_Value()); PropHelper::InitializeImage( newImage ); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newImage ); imageNode->SetName(name.toStdString().c_str()); GetDataStorage()->Add(imageNode); } void QmitkPreprocessingView::ExtractB0() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( !isDiffusionImage ) { return; } // call the extraction withou averaging if the check-box is checked if( this->m_Controls->m_CheckExtractAll->isChecked() ) { DoExtractBOWithoutAveraging(); return; } ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); // Extract image using found index typedef itk::B0ImageExtractionImageFilter FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput( itkVectorImagePointer ); filter->SetDirections(PropHelper::GetGradientContainer(image)); filter->Update(); mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk( filter->GetOutput() ); mitkImage->SetVolume( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer newNode=mitk::DataNode::New(); newNode->SetData( mitkImage ); newNode->SetProperty( "name", mitk::StringProperty::New(node->GetName() + "_B0")); GetDataStorage()->Add(newNode, node); } void QmitkPreprocessingView::DoExtractBOWithoutAveraging() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( !isDiffusionImage ) { return; } // typedefs typedef itk::B0ImageExtractionToSeparateImageFilter< short, short> FilterType; ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); // Extract image using found index FilterType::Pointer filter = FilterType::New(); filter->SetInput( itkVectorImagePointer ); filter->SetDirections(PropHelper::GetGradientContainer(image)); filter->Update(); mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk( filter->GetOutput() ); mitkImage->SetImportChannel( filter->GetOutput()->GetBufferPointer() ); mitk::DataNode::Pointer newNode=mitk::DataNode::New(); newNode->SetData( mitkImage ); newNode->SetProperty( "name", mitk::StringProperty::New(node->GetName() + "_B0_ALL")); GetDataStorage()->Add(newNode, node); /*A reinitialization is needed to access the time channels via the ImageNavigationController The Global-Geometry can not recognize the time channel without a re-init. (for a new selection in datamanger a automatically updated of the Global-Geometry should be done - if it contains the time channel)*/ mitk::RenderingManager::GetInstance()->InitializeViews( newNode->GetData()->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true); } void QmitkPreprocessingView::AverageGradients() { mitk::DataNode::Pointer node = m_Controls->m_SelctedImageComboBox->GetSelectedNode(); if (node.IsNull()) { return; } mitk::Image::Pointer image = dynamic_cast(node->GetData()); if ( image == nullptr ) { return; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( !isDiffusionImage ) return; mitk::Image::Pointer newDwi = image->Clone(); PropHelper::AverageRedundantGradients(newDwi, m_Controls->m_Blur->value()); PropHelper::InitializeImage(newDwi); mitk::DataNode::Pointer imageNode = mitk::DataNode::New(); imageNode->SetData( newDwi ); QString name = node->GetName().c_str(); imageNode->SetName((name+"_averaged").toStdString().c_str()); GetDataStorage()->Add(imageNode, node); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.python/files.cmake b/Plugins/org.mitk.gui.qt.diffusionimaging.python/files.cmake index a99ac9995d..736c7682d6 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.python/files.cmake +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.python/files.cmake @@ -1,49 +1,49 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkBrainExtractionView.cpp QmitkDipyReconstructionsView.cpp QmitkTractSegView.cpp ) set(UI_FILES src/internal/QmitkBrainExtractionViewControls.ui src/internal/QmitkDipyReconstructionsViewControls.ui src/internal/QmitkTractSegViewControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkBrainExtractionView.h src/internal/QmitkDipyReconstructionsView.h src/internal/QmitkTractSegView.h ) set(CACHED_RESOURCE_FILES plugin.xml resources/tract_seg.png resources/brain_extraction.png resources/dipy.png ) set(QRC_FILES - resources/diffusion_python_resources.qrc + resources/QmitkDiffusionPython.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.python/plugin.xml b/Plugins/org.mitk.gui.qt.diffusionimaging.python/plugin.xml index de706ba265..74b3161e1c 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.python/plugin.xml +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.python/plugin.xml @@ -1,33 +1,48 @@ + + + + + + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.python/resources/diffusion_python_resources.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging.python/resources/QmitkDiffusionPython.qrc similarity index 69% rename from Plugins/org.mitk.gui.qt.diffusionimaging.python/resources/diffusion_python_resources.qrc rename to Plugins/org.mitk.gui.qt.diffusionimaging.python/resources/QmitkDiffusionPython.qrc index ab90055058..d966a74575 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.python/resources/diffusion_python_resources.qrc +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.python/resources/QmitkDiffusionPython.qrc @@ -1,6 +1,6 @@ - + dipy_reconstructions.py tractseg.py diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/resources/QmitkDiffusionImaging.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/resources/QmitkDiffusionImaging.qrc deleted file mode 100644 index cb3bc7bf16..0000000000 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.reconstruction/resources/QmitkDiffusionImaging.qrc +++ /dev/null @@ -1,7 +0,0 @@ - - - tensor.png - quantification.png - odf.png - - diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/documentation/UserManual/QmitkStreamlineTrackingViewUserManual.dox b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/documentation/UserManual/QmitkStreamlineTrackingViewUserManual.dox index 8c9322383e..6cc4616fd8 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/documentation/UserManual/QmitkStreamlineTrackingViewUserManual.dox +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/documentation/UserManual/QmitkStreamlineTrackingViewUserManual.dox @@ -1,102 +1,105 @@ /** \page org_mitk_views_streamlinetracking Streamline Tractography This view enables streamline tractography on various input data. The corresponding command line application is named "MitkStreamlineTractography". Available sections: - \ref StrTrackUserManualInputData - \ref StrTrackUserManualSeeding - \ref StrTrackUserManualConstraints - \ref StrTrackUserManualParameters - \ref StrTrackUserManualNeighbourhoodSampling - \ref StrTrackUserManualDataHandling - \ref StrTrackUserManualPostprocessing - \ref StrTrackUserManualReferences \section StrTrackUserManualInputData Input Data Select the data you want to track on in the datamanager. Supported file types are: - One or multiple DTI images selected in the datamanager. - One ODF image, e.g. obtained using MITK Q-ball reconstruction or MRtrix CSD (tractography similar to [6]). - One peak image (4D float image). - One raw diffusion-weighted image for machine learning based tractography [1]. -- Tractography Forest: Needed for machine learning based tractography [1]. \section StrTrackUserManualSeeding Seeding Specify how, where and how many tractography seed points are placed. This can be either done statically using a seed image or in an interactive fashion. Interactive tractography enables the dynamic placement of spherical seed regions simply by clicking into the image (similar to [5]). Image based seeding: - Seed Image: ROI image used to define the seed voxels. If no seed mask is specified, the whole image volume is seeded. - Seeds per voxel: If set to 1, the seed is defined as the voxel center. If > 1 the seeds are distributet randomly inside the voxel. Interactive seeding: - Update on Parameter Change: When "Update on Parameter Change" is checked, each parameter change causes an instant retracking with the new parameters. This enables an intuitive exploration of the effects that the other tractography parameters have on the resulting tractogram. - Radius: Radius of the manually placed spherical seed region. - Num.Seeds: Number of seeds placed randomly inside the spherical seed region. Parameters for both seeding modes: - Trials Per Seed: Try each seed N times until a valid streamline is obtained (only for probabilistic tractography). - Max. Num. Fibers: Tractography is stopped after the desired number of fibers is reached, even before all seed points are processed. \section StrTrackUserManualConstraints ROI Constraints Specify various ROI and mask images to constrain the tractography process. - Mask Image: ROI image used to constrain the generated streamlines, typically a brain mask. Streamlines that leave the regions defined in this image will stop immediately. - Stop ROI Image: ROI image used to define stopping regions. Streamlines that enter the regions defined in this image will stop immediately. - Exclusion ROI Image: Fibers that enter a region defined in this image will be discarded. - Endpoint Constraints: Determines which fibers are accepted based on their endpoint location. Options are: - No constraints on endpoint locations (command line option NONE) - Both EPs are required to be located in the target image (command line option EPS_IN_TARGET) - Both EPs are required to be located in the target image and the image values at the respective position needs to be distinct (command line option EPS_IN_TARGET_LABELDIFF) - One EP is required to be located in the seed image and one in the target image (command line option EPS_IN_SEED_AND_TARGET) - At least one EP is required to be located in the target image (command line option MIN_ONE_EP_IN_TARGET) - Exactly one EP is required to be located in the target image (command line option ONE_EP_IN_TARGET) - No EP is allowed to be located in the target image (command line option NO_EP_IN_TARGET) - Target Image: ROI image needed for endpoint constraints. \section StrTrackUserManualParameters Tractography Parameters -- Mode: Toggle between deterministic and probabilistic tractography. Peak tracking only supports deterministic mode. The probabilistic method simply samples the output direction from the discrete probability ditribution provided by the discretized ODF. +- Mode: Toggle between deterministic and probabilistic tractography (also affects tracking prior proposals). The probabilistic method simply samples the output direction from the discrete probability ditribution provided by the discretized ODF. Probabilistic peak tracking does not derive probabilities from the data but simply adds a normally distributed jitter to the proposed direction. - Sharpen ODFs: If you are using dODF images as input, it is advisable to sharpen the ODFs (min-max normalize and raise to the power of 4). This is not necessary (and not recommended) for CSD fODFs, since they are naturally much sharper. - Cutoff: If the streamline reaches a position with an FA value or peak magnitude lower than the speciefied threshold, tracking is terminated. Typical values are 0.2 for FA/GFA and 0.1 for CSD peaks. - FA/GFA image used to determine streamline termination. If no image is specified, the FA/GFA image is automatically calculated from the input image. If multiple tensor images are used as input, it is recommended to provide such an image since the FA maps calculated from the individual input tensor images can not provide a suitable termination criterion. - ODF Cutoff: Additional threshold on the ODF magnitude. This is useful in case of CSD fODF tractography. For fODFs a good default value is 0.1, for normalized dODFs, e.g. Q-ball ODFs, this threshold should be very low (0.00025) or 0. - Step Size: The algorithm proceeds along the streamline with a fixed stepsize. Default is 0.5*minSpacing. - Min. Tract Length: Shorter fibers are discarded. -- Angular threshold: Maximum angle between two successive steps (in degree). Default is 90° * step_size. For probabilistic tractography, candidate directions exceeding this threshold have probability 0, i.e. the respective ODF value is set to zero. The probabilities of the valid directions are normalized to sum to 1. +- Max. Tract Length: Longer fibers are discarded. +- Angular Threshold: Maximum angle between two successive steps (in degree). Default is 90° * step_size. For probabilistic tractography, candidate directions exceeding this threshold have probability 0, i.e. the respective ODF value is set to zero. The probabilities of the valid directions are normalized to sum to 1. - Loop Check: Stop streamline if the threshold on the angular stdev over the last 4 voxel lengths is exceeded. -1 = no loop check. - f and g values to balance between FACT [2] and TEND [3,4] tracking (only for tensor based tractography). For further information please refer to [2,3] +- Peak Jitter: Used for probabilistic peak tractography. Since no probability are provided by the data, a gausian jitter is added to the peaks. The value influences the standard deviataion of the gaussian (dir[i] += normal(0, peak_jitter*|dir[i]|)). \section StrTrackUserManualTractographyPrior Tractography Prior It is possible to use a peak image as prior for tractography on arbitrary other input images. The local progression direction is determined as the weighted average between the direction obtained from the prior and the input data. - Weight: Weighting factor between prior and input data directions. A weight of zero means that no prior iformation is used. With a weight of one, tractography is performed directly on the prior directions itself. - Restrict to Prior: The prior image is used as tractography mask. Voxels without prior peaks are excluded. - New Directions from Prior: By default, the prior is used even if there is no valid direction found in the data. If unchecked, the prior cannot create directions where there are none in the data. +- Flip directions: Internally flips prior directions. This might be necessary depending on the input data. \section StrTrackUserManualDataHandling Data Handling - Flip directions: Internally flips progression directions. This might be necessary depending on the input data. - Interpolate Tractography Data: Trilinearly interpolate the input image used for tractography. - Interpolate ROI Images: Trilinearly interpolate the ROI images used to constrain the tractography. \section StrTrackUserManualNeighbourhoodSampling Neighbourhood Sampling (for details see [1]) - Neighborhood Samples: Number of neighborhood samples that are used to determine the next fiber progression direction. - Sampling Distance: Distance of the sampling positions from the current streamline position (in voxels). - Use Only Frontal Samples: Only neighborhood samples in front of the current streamline position are considered. - Use Stop-Votes: If checked, the majority of sampling points has to place a stop-vote for the streamline to terminate. If not checked, all sampling positions have to vote for a streamline termination. \section StrTrackUserManualPostprocessing Output and Postprocessing - Compress Fibers: Whole brain tractograms obtained with a small step size can contain billions of points. The tractograms can be compressed by removing points that do not really contribute to the fiber shape, such as many points on a straight line. An error threshold (in mm) can be defined to specify which points should be removed and which not. - Output Probability Map: No streamline are generated. Instead, the tractography outputs a visitation-count map that indicates the probability of a fiber to reach a voxel from the selected seed region. For this measure to be sensible, the number of seeds per voxel needs to be rather large. \section StrTrackUserManualReferences References [1] Neher, Peter F., Marc-Alexandre Côté, Jean-Christophe Houde, Maxime Descoteaux, and Klaus H. Maier-Hein. “Fiber Tractography Using Machine Learning.†NeuroImage. Accessed July 19, 2017. doi:10.1016/j.neuroimage.2017.07.028.\n [2] Mori, Susumu, Walter E. Kaufmann, Godfrey D. Pearlson, Barbara J. Crain, Bram Stieltjes, Meiyappan Solaiyappan, and Peter C. M. Van Zijl. “In Vivo Visualization of Human Neural Pathways by Magnetic Resonance Imaging.†Annals of Neurology 47 (2000): 412–414.\n [3] Weinstein, David, Gordon Kindlmann, and Eric Lundberg. “Tensorlines: Advection-Diffusion Based Propagation through Diffusion Tensor Fields.†In Proceedings of the Conference on Visualization’99: Celebrating Ten Years, 249–253, n.d.\n [4] Lazar, Mariana, David M. Weinstein, Jay S. Tsuruda, Khader M. Hasan, Konstantinos Arfanakis, M. Elizabeth Meyerand, Benham Badie, et al. “White Matter Tractography Using Diffusion Tensor Deflection.†Human Brain Mapping 18, no. 4 (2003): 306–321.\n [5] Chamberland, M., K. Whittingstall, D. Fortin, D. Mathieu, and M. Descoteaux. “Real-Time Multi-Peak Tractography for Instantaneous Connectivity Display.†Front Neuroinform 8 (2014): 59. doi:10.3389/fninf.2014.00059.\n [6] Tournier, J-Donald, Fernando Calamante, and Alan Connelly. “MRtrix: Diffusion Tractography in Crossing Fiber Regions.†International Journal of Imaging Systems and Technology 22, no. 1 (March 2012): 53–66. doi:10.1002/ima.22005. */ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/files.cmake b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/files.cmake index 42137f0e9c..b29fbe3471 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/files.cmake +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/files.cmake @@ -1,58 +1,51 @@ -set(SRC_CPP_FILES - QmitkMlbstTrainingDataWidget.cpp -) - set(INTERNAL_CPP_FILES QmitkGibbsTrackingView.cpp QmitkStreamlineTrackingView.cpp - QmitkMLBTView.cpp Perspectives/QmitkGibbsTractographyPerspective.cpp Perspectives/QmitkStreamlineTractographyPerspective.cpp mitkPluginActivator.cpp ) set(UI_FILES src/internal/QmitkGibbsTrackingViewControls.ui src/internal/QmitkStreamlineTrackingViewControls.ui - src/internal/QmitkMLBTViewControls.ui - - src/QmitkMlbstTrainingDataWidgetControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkGibbsTrackingView.h src/internal/QmitkStreamlineTrackingView.h - src/internal/QmitkMLBTView.h - - src/QmitkMlbstTrainingDataWidget.h src/internal/Perspectives/QmitkGibbsTractographyPerspective.h src/internal/Perspectives/QmitkStreamlineTractographyPerspective.h ) set(CACHED_RESOURCE_FILES plugin.xml resources/tract.png resources/tractogram.png resources/ml_tractography.png + resources/download.png + resources/right.png + resources/stop.png + resources/upload.png ) set(QRC_FILES - + resources/QmitkTractography.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/QmitkTractography.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/QmitkTractography.qrc new file mode 100644 index 0000000000..4bfc5c2a11 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/QmitkTractography.qrc @@ -0,0 +1,8 @@ + + + upload.png + download.png + right.png + stop.png + + diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/download.png b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/download.png new file mode 100644 index 0000000000..5949729192 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/download.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/right.png b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/right.png new file mode 100644 index 0000000000..3201e76d25 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/right.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/stop.png b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/stop.png new file mode 100644 index 0000000000..108c60b1a1 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/stop.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/upload.png b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/upload.png new file mode 100644 index 0000000000..6abef3f7d7 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/resources/upload.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/QmitkMlbstTrainingDataWidget.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/QmitkMlbstTrainingDataWidget.cpp deleted file mode 100644 index 94f525fd33..0000000000 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/QmitkMlbstTrainingDataWidget.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -//Qmitk headers -#include "QmitkMlbstTrainingDataWidget.h" - -#include -#include -#include -#include -#include -#include -#include - -const std::string QmitkMlbstTrainingDataWidget::VIEW_ID = "org.mitk.views.DotModelParametersWidget"; - -QmitkMlbstTrainingDataWidget::QmitkMlbstTrainingDataWidget( QWidget * parent, Qt::WindowFlags ) - : QWidget(parent) -{ - m_Controls = nullptr; - this->CreateQtPartControl(this); -} - -QmitkMlbstTrainingDataWidget::~QmitkMlbstTrainingDataWidget() -{ -} - -void QmitkMlbstTrainingDataWidget::CreateQtPartControl(QWidget *parent) -{ - if (!m_Controls) - { - // create GUI widgets - m_Controls = new Ui::QmitkMlbstTrainingDataWidgetControls; - m_Controls->setupUi(parent); - - mitk::NodePredicateIsDWI::Pointer isDiffusionImage = mitk::NodePredicateIsDWI::New(); - - mitk::TNodePredicateDataType::Pointer isFib = mitk::TNodePredicateDataType::New(); - - m_Controls->image->SetPredicate(isDiffusionImage); - m_Controls->fibers->SetPredicate(isFib); - - mitk::TNodePredicateDataType::Pointer isMitkImage = mitk::TNodePredicateDataType::New(); - mitk::NodePredicateNot::Pointer noDiffusionImage = mitk::NodePredicateNot::New(isDiffusionImage); - mitk::NodePredicateAnd::Pointer finalPredicate = mitk::NodePredicateAnd::New(isMitkImage, noDiffusionImage); - mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); - finalPredicate = mitk::NodePredicateAnd::New(finalPredicate, isBinaryPredicate); - - m_Controls->mask->SetPredicate(finalPredicate); - m_Controls->whiteMatter->SetPredicate(finalPredicate); - } -} diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/QmitkMlbstTrainingDataWidget.h b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/QmitkMlbstTrainingDataWidget.h deleted file mode 100644 index e728c4810a..0000000000 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/QmitkMlbstTrainingDataWidget.h +++ /dev/null @@ -1,69 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ -#ifndef _QmitkMlbstTrainingDataWidget_H_INCLUDED -#define _QmitkMlbstTrainingDataWidget_H_INCLUDED - -//QT headers -#include -#include -#include "ui_QmitkMlbstTrainingDataWidgetControls.h" -#include -#include -#include - -/** @brief - */ -class DIFFUSIONIMAGING_TRACTOGRAPHY_EXPORT QmitkMlbstTrainingDataWidget : public QWidget -{ - //this is needed for all Qt objects that should have a MOC object (everything that derives from QObject) - Q_OBJECT - -public: - - static const std::string VIEW_ID; - - QmitkMlbstTrainingDataWidget (QWidget* parent = nullptr, Qt::WindowFlags f = nullptr); - - virtual ~QmitkMlbstTrainingDataWidget(); - - virtual void CreateQtPartControl(QWidget *parent); - - mitk::DataNode::Pointer GetImage(){ return m_Controls->image->GetSelectedNode(); } - mitk::DataNode::Pointer GetFibers(){ return m_Controls->fibers->GetSelectedNode(); } - mitk::DataNode::Pointer GetMask(){ return m_Controls->mask->GetSelectedNode(); } - mitk::DataNode::Pointer GetWhiteMatter(){ return m_Controls->whiteMatter->GetSelectedNode(); } - void SetDataStorage(mitk::DataStorage::Pointer ds) - { - m_Controls->image->SetDataStorage(ds); - m_Controls->fibers->SetDataStorage(ds); - m_Controls->mask->SetDataStorage(ds); - m_Controls->whiteMatter->SetDataStorage(ds); - m_Controls->mask->SetZeroEntryText("--"); - m_Controls->whiteMatter->SetZeroEntryText("--"); - } - -public slots: - -protected: - // member variables - Ui::QmitkMlbstTrainingDataWidgetControls* m_Controls; - -private: - -}; - -#endif // _QmitkMlbstTrainingDataWidget_H_INCLUDED - diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/QmitkMlbstTrainingDataWidgetControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/QmitkMlbstTrainingDataWidgetControls.ui deleted file mode 100644 index b8b4cfe501..0000000000 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/QmitkMlbstTrainingDataWidgetControls.ui +++ /dev/null @@ -1,79 +0,0 @@ - - - QmitkMlbstTrainingDataWidgetControls - - - - 0 - 0 - 349 - 27 - - - - - 0 - 0 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QComboBox::AdjustToMinimumContentsLength - - - - - - - QComboBox::AdjustToMinimumContentsLength - - - - - - - QComboBox::AdjustToMinimumContentsLength - - - - - - - QComboBox::AdjustToMinimumContentsLength - - - - - - - - QmitkDataStorageComboBox - QComboBox -
QmitkDataStorageComboBox.h
-
- - QmitkDataStorageComboBoxWithSelectNone - QComboBox -
QmitkDataStorageComboBoxWithSelectNone.h
-
-
- - -
diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkMLBTView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkMLBTView.cpp deleted file mode 100644 index 3a017d6b22..0000000000 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkMLBTView.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4244) -#endif - -// Blueberry -#include -#include - -// Qmitk -#include "QmitkMLBTView.h" - -// Qt -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define _USE_MATH_DEFINES -#include - -const std::string QmitkMLBTView::VIEW_ID = "org.mitk.views.mlbtview"; -using namespace berry; - -QmitkMLBTView::QmitkMLBTView() - : QmitkAbstractView() - , m_Controls( 0 ) -{ -} - -// Destructor -QmitkMLBTView::~QmitkMLBTView() -{ - delete m_ForestHandler; -} - -void QmitkMLBTView::CreateQtPartControl( QWidget *parent ) -{ - // build up qt view, unless already done - if ( !m_Controls ) - { - // create GUI widgets from the Qt Designer's .ui file - m_Controls = new Ui::QmitkMLBTViewControls; - m_Controls->setupUi( parent ); - - connect( m_Controls->m_StartTrainingButton, SIGNAL ( clicked() ), this, SLOT( StartTrainingThread() ) ); - connect( &m_TrainingWatcher, SIGNAL ( finished() ), this, SLOT( OnTrainingThreadStop() ) ); - connect( m_Controls->m_AddTwButton, SIGNAL ( clicked() ), this, SLOT( AddTrainingWidget() ) ); - connect( m_Controls->m_RemoveTwButton, SIGNAL ( clicked() ), this, SLOT( RemoveTrainingWidget() ) ); - - mitk::NodePredicateIsDWI::Pointer isDiffusionImage = mitk::NodePredicateIsDWI::New(); - - mitk::TNodePredicateDataType::Pointer isMitkImage = mitk::TNodePredicateDataType::New(); - mitk::NodePredicateNot::Pointer noDiffusionImage = mitk::NodePredicateNot::New(isDiffusionImage); - mitk::NodePredicateAnd::Pointer finalPredicate = mitk::NodePredicateAnd::New(isMitkImage, noDiffusionImage); - mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); - finalPredicate = mitk::NodePredicateAnd::New(finalPredicate, isBinaryPredicate); - AddTrainingWidget(); - } - -} - -void QmitkMLBTView::SetFocus() -{ -} - -void QmitkMLBTView::AddTrainingWidget() -{ - std::shared_ptr tw = std::make_shared(); - tw->SetDataStorage(this->GetDataStorage()); - m_Controls->m_TwFrame->layout()->addWidget(tw.get()); - m_TrainingWidgets.push_back(tw); -} - -void QmitkMLBTView::RemoveTrainingWidget() -{ - if(m_TrainingWidgets.size()>1) - { - m_TrainingWidgets.back().reset(); - m_TrainingWidgets.pop_back(); - } -} - -void QmitkMLBTView::StartTrainingThread() -{ - if (!this->IsTrainingInputValid()) - { - QMessageBox::warning(nullptr, "Training aborted", "Training could not be started. Not all necessary datasets were selected."); - return; - } - - if (m_Controls->m_FeatureTypeBox->currentIndex()==0) - m_ForestHandler = new mitk::TrackingHandlerRandomForest<6,28>(); - else - m_ForestHandler = new mitk::TrackingHandlerRandomForest<6,100>(); - - QFuture future = QtConcurrent::run( this, &QmitkMLBTView::StartTraining ); - m_TrainingWatcher.setFuture(future); - m_Controls->m_StartTrainingButton->setEnabled(false); - m_Controls->m_StartTrainingButton->setText("Training in progress ..."); - m_Controls->m_StartTrainingButton->setToolTip("Training in progress. This can take up to a couple of hours."); - m_Controls->m_StartTrainingButton->setCursor(Qt::WaitCursor); - QApplication::processEvents(); -} - -void QmitkMLBTView::OnTrainingThreadStop() -{ - m_Controls->m_StartTrainingButton->setEnabled(true); - m_Controls->m_StartTrainingButton->setCursor(Qt::ArrowCursor); - m_Controls->m_StartTrainingButton->setText("Start Training"); - m_Controls->m_StartTrainingButton->setToolTip("Start Training. This can take up to a couple of hours."); - - mitk::DataNode::Pointer node = mitk::DataNode::New(); - - switch (m_Controls->m_FeatureTypeBox->currentIndex()) - { - case 0: - if (dynamic_cast*>(m_ForestHandler)->IsForestValid()) - node->SetData(dynamic_cast*>(m_ForestHandler)->GetForest()); - break; - case 1: - if (dynamic_cast*>(m_ForestHandler)->IsForestValid()) - node->SetData(dynamic_cast*>(m_ForestHandler)->GetForest()); - break; - default: - mitkThrow() << "Unknown feature type!"; - } - - QString name("Forest"); - node->SetName(name.toStdString()); - GetDataStorage()->Add(node); - - delete m_ForestHandler; - - QApplication::processEvents(); -} - -void QmitkMLBTView::StartTraining() -{ - std::vector< mitk::Image::Pointer > m_SelectedDiffImages; - std::vector< mitk::FiberBundle::Pointer > m_SelectedFB; - std::vector< ItkUcharImgType::Pointer > m_MaskImages; - std::vector< ItkUcharImgType::Pointer > m_WhiteMatterImages; - - for (auto w : m_TrainingWidgets) - { - m_SelectedDiffImages.push_back(dynamic_cast(w->GetImage()->GetData())); - m_SelectedFB.push_back(dynamic_cast(w->GetFibers()->GetData())); - - if (w->GetMask().IsNotNull()) - { - mitk::Image::Pointer img = dynamic_cast(w->GetMask()->GetData()); - ItkUcharImgType::Pointer itkMask = ItkUcharImgType::New(); - mitk::CastToItkImage(img, itkMask); - m_MaskImages.push_back(itkMask); - } - else - m_MaskImages.push_back(nullptr); - - if (w->GetWhiteMatter().IsNotNull()) - { - mitk::Image::Pointer img = dynamic_cast(w->GetWhiteMatter()->GetData()); - ItkUcharImgType::Pointer itkMask = ItkUcharImgType::New(); - mitk::CastToItkImage(img, itkMask); - m_WhiteMatterImages.push_back(itkMask); - } - else - m_WhiteMatterImages.push_back(nullptr); - } - - switch (m_Controls->m_FeatureTypeBox->currentIndex()) - { - case 0: - dynamic_cast*>(m_ForestHandler)->SetDwis(m_SelectedDiffImages); - dynamic_cast*>(m_ForestHandler)->SetTractograms(m_SelectedFB); - dynamic_cast*>(m_ForestHandler)->SetMaskImages(m_MaskImages); - dynamic_cast*>(m_ForestHandler)->SetWhiteMatterImages(m_WhiteMatterImages); - dynamic_cast*>(m_ForestHandler)->SetNumTrees(m_Controls->m_NumTreesBox->value()); - dynamic_cast*>(m_ForestHandler)->SetMaxTreeDepth(m_Controls->m_MaxDepthBox->value()); - dynamic_cast*>(m_ForestHandler)->SetGrayMatterSamplesPerVoxel(m_Controls->m_GmSamplingBox->value()); - dynamic_cast*>(m_ForestHandler)->SetSampleFraction(m_Controls->m_SampleFractionBox->value()); - dynamic_cast*>(m_ForestHandler)->SetFiberSamplingStep(m_Controls->m_TrainingStepSizeBox->value()); - dynamic_cast*>(m_ForestHandler)->SetNumPreviousDirections(m_Controls->m_NumPrevDirs->value()); - dynamic_cast*>(m_ForestHandler)->StartTraining(); - break; - case 1: - dynamic_cast*>(m_ForestHandler)->SetDwis(m_SelectedDiffImages); - dynamic_cast*>(m_ForestHandler)->SetTractograms(m_SelectedFB); - dynamic_cast*>(m_ForestHandler)->SetMaskImages(m_MaskImages); - dynamic_cast*>(m_ForestHandler)->SetWhiteMatterImages(m_WhiteMatterImages); - dynamic_cast*>(m_ForestHandler)->SetNumTrees(m_Controls->m_NumTreesBox->value()); - dynamic_cast*>(m_ForestHandler)->SetMaxTreeDepth(m_Controls->m_MaxDepthBox->value()); - dynamic_cast*>(m_ForestHandler)->SetGrayMatterSamplesPerVoxel(m_Controls->m_GmSamplingBox->value()); - dynamic_cast*>(m_ForestHandler)->SetSampleFraction(m_Controls->m_SampleFractionBox->value()); - dynamic_cast*>(m_ForestHandler)->SetFiberSamplingStep(m_Controls->m_TrainingStepSizeBox->value()); - dynamic_cast*>(m_ForestHandler)->SetNumPreviousDirections(m_Controls->m_NumPrevDirs->value()); - dynamic_cast*>(m_ForestHandler)->StartTraining(); - break; - default: - mitkThrow() << "Unknown feature type!"; - } -} - -bool QmitkMLBTView::IsTrainingInputValid(void) const -{ - for (auto widget : m_TrainingWidgets) - { - if (widget->GetImage().IsNull() || widget->GetFibers().IsNull()) - { - return false; - } - } - - return true; -} - -#ifdef _MSC_VER -# pragma warning(pop) -#endif diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkMLBTView.h b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkMLBTView.h deleted file mode 100644 index 801ffd8948..0000000000 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkMLBTView.h +++ /dev/null @@ -1,88 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#include -#include "ui_QmitkMLBTViewControls.h" - -#ifndef Q_MOC_RUN -#include "mitkDataStorage.h" -#include "mitkDataStorageSelection.h" -#include -#include -#endif - -#include -#include -#include -#include -#include -#include - -/*! -\brief View to perform machine learning based fiber tractography. Includes training of the random forst classifier as well as the actual tractography. -*/ - -// Forward Qt class declarations - - -class QmitkMLBTView : public QmitkAbstractView -{ - - - // this is needed for all Qt objects that should have a Qt meta-object - // (everything that derives from QObject and wants to have signal/slots) - Q_OBJECT - -public: - - static const std::string VIEW_ID; - - typedef itk::Image ItkUcharImgType; - typedef itk::StreamlineTrackingFilter TrackerType; - - QmitkMLBTView(); - virtual ~QmitkMLBTView(); - virtual void CreateQtPartControl(QWidget *parent) override; - - /// - /// Sets the focus to an internal widget. - /// - virtual void SetFocus() override; - -protected slots: - - void StartTrainingThread(); - void OnTrainingThreadStop(); - void AddTrainingWidget(); - void RemoveTrainingWidget(); - -protected: - - void StartTraining(); - - Ui::QmitkMLBTViewControls* m_Controls; - mitk::TrackingDataHandler* m_ForestHandler; - QFutureWatcher m_TrainingWatcher; - std::vector< std::shared_ptr > m_TrainingWidgets; - -private: - - bool IsTrainingInputValid(void) const; - -}; - - - diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkMLBTViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkMLBTViewControls.ui deleted file mode 100644 index eea4153d87..0000000000 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkMLBTViewControls.ui +++ /dev/null @@ -1,444 +0,0 @@ - - - QmitkMLBTViewControls - - - - 0 - 0 - 476 - 548 - - - - Form - - - QCommandLinkButton:disabled { - border: none; -} - -QGroupBox { - background-color: transparent; -} - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Remove training data pair - - - ... - - - - :/org_mitk_icons/icons/tango/scalable/actions/list-remove.svg -:/org_mitk_icons/icons/tango/scalable/actions/list-remove.svg - - - - - - - Add additional training data pair - - - ... - - - - :/org_mitk_icons/icons/tango/scalable/actions/list-add.svg -:/org_mitk_icons/icons/tango/scalable/actions/list-add.svg - - - - - - - - - - <html><head/><body><p><span style=" font-weight:600; color:#ff0000;">It is recommended to use the command line application 'RfTraining' instead of this graphical user interface for the training process. Additional feature images are not supported here.</span></p><p><span style=" font-weight:600; color:#ff0000;">The GUI is intended for testing using small examples.</span></p></body></html> - - - Qt::RichText - - - Qt::AlignCenter - - - true - - - 5 - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - 6 - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Input DWI: - - - Qt::AlignCenter - - - - - - - Reference Tractogram: - - - Qt::AlignCenter - - - - - - - Mask: - - - Qt::AlignCenter - - - - - - - WM: - - - Qt::AlignCenter - - - - - - - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Fraction of samples used to train each tree. - - - 3 - - - 1.000000000000000 - - - 0.100000000000000 - - - 0.700000000000000 - - - - - - - Num. Trees: - - - - - - - Sample Fraction: - - - - - - - Max. Depth: - - - - - - - Fiber sampling in mm. Determines the number of white-matter samples (-1 = auto). - - - 3 - - - -1.000000000000000 - - - 999.000000000000000 - - - 0.100000000000000 - - - -1.000000000000000 - - - - - - - Number of tress in the final random forest. - - - 1 - - - 999999999 - - - 30 - - - - - - - Non-WM Sampling Points: - - - - - - - Fiber Sampling: - - - - - - - Maximum tree depth. - - - 1 - - - 999999999 - - - 25 - - - - - - - Number of sampling points outside of the white-matter (-1 = automatic estimation). - - - -1 - - - 999999999 - - - -1 - - - - - - - Num. previous directions: - - - - - - - Number of previous fiber directions used as features. - - - - - - 0 - - - 50 - - - 1 - - - - - - - dMRI Features: - - - - - - - - Spherical Harmonics Coefficients - - - - - Raw Data - - - - - - - - - - - - - - Start Training. This can take up to a couple of hours. - - - Start Training - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.cpp index ebb483faa3..3bf1d89ce3 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.cpp @@ -1,1001 +1,1159 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include #include // Qmitk #include "QmitkStreamlineTrackingView.h" #include "QmitkStdMultiWidget.h" // Qt #include +#include // MITK #include #include #include #include #include #include #include #include #include #include #include #include #include #include // VTK #include #include #include #include #include #include #include #include #include #include const std::string QmitkStreamlineTrackingView::VIEW_ID = "org.mitk.views.streamlinetracking"; const std::string id_DataManager = "org.mitk.views.datamanager"; using namespace berry; QmitkStreamlineTrackingWorker::QmitkStreamlineTrackingWorker(QmitkStreamlineTrackingView* view) : m_View(view) { } void QmitkStreamlineTrackingWorker::run() { m_View->m_Tracker->Update(); m_View->m_TrackingThread.quit(); } QmitkStreamlineTrackingView::QmitkStreamlineTrackingView() : m_TrackingWorker(this) , m_Controls(nullptr) , m_FirstTensorProbRun(true) , m_FirstInteractiveRun(true) , m_TrackingHandler(nullptr) , m_ThreadIsRunning(false) , m_DeleteTrackingHandler(false) , m_Visible(false) , m_LastPrior(nullptr) , m_TrackingPriorHandler(nullptr) { m_TrackingWorker.moveToThread(&m_TrackingThread); connect(&m_TrackingThread, SIGNAL(started()), this, SLOT(BeforeThread())); connect(&m_TrackingThread, SIGNAL(started()), &m_TrackingWorker, SLOT(run())); connect(&m_TrackingThread, SIGNAL(finished()), this, SLOT(AfterThread())); m_TrackingTimer = new QTimer(this); } // Destructor QmitkStreamlineTrackingView::~QmitkStreamlineTrackingView() { if (m_Tracker.IsNull()) return; m_Tracker->SetStopTracking(true); m_TrackingThread.wait(); } void QmitkStreamlineTrackingView::CreateQtPartControl( QWidget *parent ) { if ( !m_Controls ) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkStreamlineTrackingViewControls; m_Controls->setupUi( parent ); m_Controls->m_FaImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_SeedImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_MaskImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_TargetImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_PriorImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_StopImageSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_ForestSelectionWidget->SetDataStorage(this->GetDataStorage()); m_Controls->m_ExclusionImageSelectionWidget->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType::Pointer isPeakImagePredicate = mitk::TNodePredicateDataType::New(); mitk::TNodePredicateDataType::Pointer isImagePredicate = mitk::TNodePredicateDataType::New(); mitk::TNodePredicateDataType::Pointer isTractographyForest = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinaryPredicate = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotBinaryPredicate = mitk::NodePredicateNot::New( isBinaryPredicate ); mitk::NodePredicateAnd::Pointer isNotABinaryImagePredicate = mitk::NodePredicateAnd::New( isImagePredicate, isNotBinaryPredicate ); mitk::NodePredicateDimension::Pointer dimensionPredicate = mitk::NodePredicateDimension::New(3); m_Controls->m_ForestSelectionWidget->SetNodePredicate(isTractographyForest); m_Controls->m_FaImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isNotABinaryImagePredicate, dimensionPredicate) ); m_Controls->m_FaImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_FaImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_SeedImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isImagePredicate, dimensionPredicate) ); m_Controls->m_SeedImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_SeedImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_MaskImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isImagePredicate, dimensionPredicate) ); m_Controls->m_MaskImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_MaskImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_StopImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isImagePredicate, dimensionPredicate) ); m_Controls->m_StopImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_StopImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_TargetImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isImagePredicate, dimensionPredicate) ); m_Controls->m_TargetImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_TargetImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_PriorImageSelectionWidget->SetNodePredicate( isPeakImagePredicate ); m_Controls->m_PriorImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_PriorImageSelectionWidget->SetSelectionIsOptional(true); m_Controls->m_ExclusionImageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(isImagePredicate, dimensionPredicate) ); m_Controls->m_ExclusionImageSelectionWidget->SetEmptyInfo("--"); m_Controls->m_ExclusionImageSelectionWidget->SetSelectionIsOptional(true); connect( m_TrackingTimer, SIGNAL(timeout()), this, SLOT(TimerUpdate()) ); + connect( m_Controls->m_SaveParametersButton, SIGNAL(clicked()), this, SLOT(SaveParameters()) ); + connect( m_Controls->m_LoadParametersButton, SIGNAL(clicked()), this, SLOT(LoadParameters()) ); connect( m_Controls->commandLinkButton_2, SIGNAL(clicked()), this, SLOT(StopTractography()) ); connect( m_Controls->commandLinkButton, SIGNAL(clicked()), this, SLOT(DoFiberTracking()) ); connect( m_Controls->m_InteractiveBox, SIGNAL(stateChanged(int)), this, SLOT(ToggleInteractive()) ); connect( m_Controls->m_ModeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGui()) ); connect( m_Controls->m_FaImageSelectionWidget, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(DeleteTrackingHandler()) ); connect( m_Controls->m_ModeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(DeleteTrackingHandler()) ); connect( m_Controls->m_OutputProbMap, SIGNAL(stateChanged(int)), this, SLOT(OutputStyleSwitched()) ); connect( m_Controls->m_SeedImageSelectionWidget, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_ModeBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_StopImageSelectionWidget, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_TargetImageSelectionWidget, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_PriorImageSelectionWidget, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_ExclusionImageSelectionWidget, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_MaskImageSelectionWidget, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_FaImageSelectionWidget, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_ForestSelectionWidget, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(ForestSwitched()) ); connect( m_Controls->m_ForestSelectionWidget, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_SeedsPerVoxelBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_NumFibersBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_ScalarThresholdBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_OdfCutoffBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_StepSizeBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_SamplingDistanceBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_AngularThresholdBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_MinTractLengthBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); + connect( m_Controls->m_MaxTractLengthBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_fBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_gBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_NumSamplesBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_SeedRadiusBox, SIGNAL(editingFinished()), this, SLOT(InteractiveSeedChanged()) ); connect( m_Controls->m_NumSeedsBox, SIGNAL(editingFinished()), this, SLOT(InteractiveSeedChanged()) ); connect( m_Controls->m_OutputProbMap, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_SharpenOdfsBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_InterpolationBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_MaskInterpolationBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_FlipXBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_FlipYBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_FlipZBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); + connect( m_Controls->m_PriorFlipXBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); + connect( m_Controls->m_PriorFlipYBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); + connect( m_Controls->m_PriorFlipZBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_FrontalSamplesBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_StopVotesBox, SIGNAL(stateChanged(int)), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_LoopCheckBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_TrialsPerSeedBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); connect( m_Controls->m_EpConstraintsBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnParameterChanged()) ); + connect( m_Controls->m_PeakJitterBox, SIGNAL(editingFinished()), this, SLOT(OnParameterChanged()) ); m_Controls->m_SeedsPerVoxelBox->editingFinished(); m_Controls->m_NumFibersBox->editingFinished(); m_Controls->m_ScalarThresholdBox->editingFinished(); m_Controls->m_OdfCutoffBox->editingFinished(); m_Controls->m_StepSizeBox->editingFinished(); m_Controls->m_SamplingDistanceBox->editingFinished(); m_Controls->m_AngularThresholdBox->editingFinished(); m_Controls->m_MinTractLengthBox->editingFinished(); + m_Controls->m_MaxTractLengthBox->editingFinished(); m_Controls->m_fBox->editingFinished(); m_Controls->m_gBox->editingFinished(); m_Controls->m_NumSamplesBox->editingFinished(); m_Controls->m_SeedRadiusBox->editingFinished(); m_Controls->m_NumSeedsBox->editingFinished(); m_Controls->m_LoopCheckBox->editingFinished(); m_Controls->m_TrialsPerSeedBox->editingFinished(); + m_Controls->m_PeakJitterBox->editingFinished(); StartStopTrackingGui(false); } + m_ParameterFile = QDir::currentPath()+"/param.stp"; UpdateGui(); } +void QmitkStreamlineTrackingView::ParametersToGui(mitk::StreamlineTractographyParameters& params) +{ + m_Controls->m_SeedRadiusBox->setValue(params.m_InteractiveRadiusMm); + + m_Controls->m_NumSeedsBox->setValue(params.m_NumInteractiveSeeds); + m_Controls->m_InteractiveBox->setChecked(params.m_EnableInteractive); + m_Controls->m_FiberErrorBox->setValue(params.m_Compression); + m_Controls->m_ResampleFibersBox->setChecked(params.m_CompressFibers); + + m_Controls->m_SeedRadiusBox->setValue(params.m_InteractiveRadiusMm); + m_Controls->m_NumFibersBox->setValue(params.m_MaxNumFibers); + m_Controls->m_ScalarThresholdBox->setValue(params.m_Cutoff); + m_Controls->m_fBox->setValue(params.m_F); + m_Controls->m_gBox->setValue(params.m_G); + + m_Controls->m_OdfCutoffBox->setValue(params.m_OdfCutoff); + m_Controls->m_SharpenOdfsBox->setChecked(params.m_SharpenOdfs); + + m_Controls->m_PriorWeightBox->setValue(params.m_Weight); + m_Controls->m_PriorAsMaskBox->setChecked(params.m_RestrictToPrior); + m_Controls->m_NewDirectionsFromPriorBox->setChecked(params.m_NewDirectionsFromPrior); + + m_Controls->m_PriorFlipXBox->setChecked(params.m_PriorFlipX); + m_Controls->m_PriorFlipYBox->setChecked(params.m_PriorFlipY); + m_Controls->m_PriorFlipZBox->setChecked(params.m_PriorFlipZ); + + m_Controls->m_FlipXBox->setChecked(params.m_FlipX); + m_Controls->m_FlipYBox->setChecked(params.m_FlipY); + m_Controls->m_FlipZBox->setChecked(params.m_FlipZ); + m_Controls->m_InterpolationBox->setChecked(params.m_InterpolateTractographyData); + + + m_Controls->m_MaskInterpolationBox->setChecked(params.m_InterpolateRoiImages); + m_Controls->m_SeedsPerVoxelBox->setValue(params.m_SeedsPerVoxel); + m_Controls->m_StepSizeBox->setValue(params.GetStepSizeVox()); + m_Controls->m_SamplingDistanceBox->setValue(params.GetSamplingDistanceVox()); + m_Controls->m_StopVotesBox->setChecked(params.m_StopVotes); + m_Controls->m_FrontalSamplesBox->setChecked(params.m_OnlyForwardSamples); + m_Controls->m_TrialsPerSeedBox->setValue(params.m_TrialsPerSeed); + + m_Controls->m_NumSamplesBox->setValue(params.m_NumSamples); + m_Controls->m_LoopCheckBox->setValue(params.GetLoopCheckDeg()); + m_Controls->m_AngularThresholdBox->setValue(params.GetAngularThresholdDeg()); + m_Controls->m_MinTractLengthBox->setValue(params.m_MinTractLengthMm); + m_Controls->m_MaxTractLengthBox->setValue(params.m_MaxTractLengthMm); + m_Controls->m_OutputProbMap->setChecked(params.m_OutputProbMap); + m_Controls->m_FixSeedBox->setChecked(params.m_FixRandomSeed); + m_Controls->m_PeakJitterBox->setValue(params.m_PeakJitter); + + switch (params.m_Mode) + { + case mitk::TrackingDataHandler::MODE::DETERMINISTIC: + m_Controls->m_ModeBox->setCurrentIndex(0); + break; + case mitk::TrackingDataHandler::MODE::PROBABILISTIC: + m_Controls->m_ModeBox->setCurrentIndex(1); + break; + } + + switch (params.m_EpConstraints) + { + case itk::StreamlineTrackingFilter::EndpointConstraints::NONE: + m_Controls->m_EpConstraintsBox->setCurrentIndex(0); + break; + case itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET: + m_Controls->m_EpConstraintsBox->setCurrentIndex(1); + break; + case itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET_LABELDIFF: + m_Controls->m_EpConstraintsBox->setCurrentIndex(2); + break; + case itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_SEED_AND_TARGET: + m_Controls->m_EpConstraintsBox->setCurrentIndex(3); + break; + case itk::StreamlineTrackingFilter::EndpointConstraints::MIN_ONE_EP_IN_TARGET: + m_Controls->m_EpConstraintsBox->setCurrentIndex(4); + break; + case itk::StreamlineTrackingFilter::EndpointConstraints::ONE_EP_IN_TARGET: + m_Controls->m_EpConstraintsBox->setCurrentIndex(5); + break; + case itk::StreamlineTrackingFilter::EndpointConstraints::NO_EP_IN_TARGET: + m_Controls->m_EpConstraintsBox->setCurrentIndex(6); + break; + } +} + +std::shared_ptr QmitkStreamlineTrackingView::GetParametersFromGui() +{ + + // NOT IN GUI +// unsigned int m_NumPreviousDirections = 1; +// bool m_AvoidStop = true; +// bool m_RandomSampling = false; +// float m_DeflectionMod = 1.0; +// bool m_ApplyDirectionMatrix = false; + + std::shared_ptr params = std::make_shared(); + params->m_InteractiveRadiusMm = m_Controls->m_SeedRadiusBox->value(); + params->m_NumInteractiveSeeds = m_Controls->m_NumSeedsBox->value(); + params->m_EnableInteractive = m_Controls->m_InteractiveBox->isChecked(); + params->m_Compression = m_Controls->m_FiberErrorBox->value(); + params->m_CompressFibers = m_Controls->m_ResampleFibersBox->isChecked(); + + params->m_InteractiveRadiusMm = m_Controls->m_SeedRadiusBox->value(); + params->m_MaxNumFibers = m_Controls->m_NumFibersBox->value(); + params->m_Cutoff = static_cast(m_Controls->m_ScalarThresholdBox->value()); + params->m_F = static_cast(m_Controls->m_fBox->value()); + params->m_G = static_cast(m_Controls->m_gBox->value()); + + params->m_OdfCutoff = static_cast(m_Controls->m_OdfCutoffBox->value()); + params->m_SharpenOdfs = m_Controls->m_SharpenOdfsBox->isChecked(); + + params->m_Weight = static_cast(m_Controls->m_PriorWeightBox->value()); + params->m_RestrictToPrior = m_Controls->m_PriorAsMaskBox->isChecked(); + params->m_NewDirectionsFromPrior = m_Controls->m_NewDirectionsFromPriorBox->isChecked(); + + params->m_PriorFlipX = m_Controls->m_PriorFlipXBox->isChecked(); + params->m_PriorFlipY = m_Controls->m_PriorFlipYBox->isChecked(); + params->m_PriorFlipZ = m_Controls->m_PriorFlipZBox->isChecked(); + + params->m_FlipX = m_Controls->m_FlipXBox->isChecked(); + params->m_FlipY = m_Controls->m_FlipYBox->isChecked(); + params->m_FlipZ = m_Controls->m_FlipZBox->isChecked(); + params->m_InterpolateTractographyData = m_Controls->m_InterpolationBox->isChecked(); + + + params->m_InterpolateRoiImages = m_Controls->m_MaskInterpolationBox->isChecked(); + params->m_SeedsPerVoxel = m_Controls->m_SeedsPerVoxelBox->value(); + params->SetStepSizeVox(m_Controls->m_StepSizeBox->value()); + params->SetSamplingDistanceVox(m_Controls->m_SamplingDistanceBox->value()); + params->m_StopVotes = m_Controls->m_StopVotesBox->isChecked(); + params->m_OnlyForwardSamples = m_Controls->m_FrontalSamplesBox->isChecked(); + params->m_TrialsPerSeed = m_Controls->m_TrialsPerSeedBox->value(); + + params->m_NumSamples = m_Controls->m_NumSamplesBox->value(); + params->SetLoopCheckDeg(m_Controls->m_LoopCheckBox->value()); + params->SetAngularThresholdDeg(m_Controls->m_AngularThresholdBox->value()); + params->m_MinTractLengthMm = m_Controls->m_MinTractLengthBox->value(); + params->m_MaxTractLengthMm = m_Controls->m_MaxTractLengthBox->value(); + params->m_OutputProbMap = m_Controls->m_OutputProbMap->isChecked(); + params->m_FixRandomSeed = m_Controls->m_FixSeedBox->isChecked(); + params->m_PeakJitter = static_cast(m_Controls->m_PeakJitterBox->value()); + + switch (m_Controls->m_ModeBox->currentIndex()) + { + case 0: + params->m_Mode = mitk::TrackingDataHandler::MODE::DETERMINISTIC; + break; + case 1: + params->m_Mode = mitk::TrackingDataHandler::MODE::PROBABILISTIC; + break; + default: + params->m_Mode = mitk::TrackingDataHandler::MODE::DETERMINISTIC; + } + + switch (m_Controls->m_EpConstraintsBox->currentIndex()) + { + case 0: + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::NONE; + break; + case 1: + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET; + break; + case 2: + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET_LABELDIFF; + break; + case 3: + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_SEED_AND_TARGET; + break; + case 4: + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::MIN_ONE_EP_IN_TARGET; + break; + case 5: + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::ONE_EP_IN_TARGET; + break; + case 6: + params->m_EpConstraints = itk::StreamlineTrackingFilter::EndpointConstraints::NO_EP_IN_TARGET; + break; + } + + return params; +} + +void QmitkStreamlineTrackingView::SaveParameters() +{ + QString filename = QFileDialog::getSaveFileName( + 0, + tr("Save Tractography Parameters"), + m_ParameterFile, + tr("Streamline Tractography Parameters (*.stp)") ); + + if(filename.isEmpty() || filename.isNull()) + return; + + m_ParameterFile = filename; + + auto params = GetParametersFromGui(); + params->SaveParameters(m_ParameterFile.toStdString()); +} + +void QmitkStreamlineTrackingView::LoadParameters() +{ + QString filename = QFileDialog::getOpenFileName( + 0, + tr("Load Tractography Parameters"), + m_ParameterFile, + tr("Streamline Tractography Parameters (*.stp)") ); + + if(filename.isEmpty() || filename.isNull()) + return; + + m_ParameterFile = filename; + + mitk::StreamlineTractographyParameters params; + params.LoadParameters(m_ParameterFile.toStdString()); + ParametersToGui(params); +} + void QmitkStreamlineTrackingView::StopTractography() { if (m_Tracker.IsNull()) return; m_Tracker->SetStopTracking(true); } void QmitkStreamlineTrackingView::TimerUpdate() { if (m_Tracker.IsNull()) return; QString status_text(m_Tracker->GetStatusText().c_str()); m_Controls->m_StatusTextBox->setText(status_text); } void QmitkStreamlineTrackingView::BeforeThread() { m_TrackingTimer->start(1000); } void QmitkStreamlineTrackingView::AfterThread() { + auto params = m_Tracker->GetParameters(); m_TrackingTimer->stop(); - if (!m_Tracker->GetUseOutputProbabilityMap()) + if (!params->m_OutputProbMap) { vtkSmartPointer fiberBundle = m_Tracker->GetFiberPolyData(); if (!m_Controls->m_InteractiveBox->isChecked() && fiberBundle->GetNumberOfLines() == 0) { QMessageBox warnBox; warnBox.setWindowTitle("Warning"); warnBox.setText("No fiberbundle was generated!"); warnBox.setDetailedText("No fibers were generated using the chosen parameters. Typical reasons are:\n\n- Cutoff too high. Some images feature very low FA/GFA/peak size. Try to lower this parameter.\n- Angular threshold too strict. Try to increase this parameter.\n- A small step sizes also means many steps to go wrong. Especially in the case of probabilistic tractography. Try to adjust the angular threshold."); warnBox.setIcon(QMessageBox::Warning); warnBox.exec(); if (m_InteractivePointSetNode.IsNotNull()) m_InteractivePointSetNode->SetProperty("color", mitk::ColorProperty::New(1,1,1)); StartStopTrackingGui(false); if (m_DeleteTrackingHandler) DeleteTrackingHandler(); UpdateGui(); return; } mitk::FiberBundle::Pointer fib = mitk::FiberBundle::New(fiberBundle); fib->SetReferenceGeometry(dynamic_cast(m_ParentNode->GetData())->GetGeometry()); if (m_Controls->m_ResampleFibersBox->isChecked() && fiberBundle->GetNumberOfLines()>0) fib->Compress(m_Controls->m_FiberErrorBox->value()); fib->ColorFibersByOrientation(); m_Tracker->SetDicomProperties(fib); if (m_Controls->m_InteractiveBox->isChecked()) { if (m_InteractiveNode.IsNull()) { m_InteractiveNode = mitk::DataNode::New(); QString name("Interactive"); m_InteractiveNode->SetName(name.toStdString()); GetDataStorage()->Add(m_InteractiveNode); } m_InteractiveNode->SetData(fib); - m_InteractiveNode->SetFloatProperty("Fiber2DSliceThickness", m_Tracker->GetMinVoxelSize()/2); + m_InteractiveNode->SetFloatProperty("Fiber2DSliceThickness", params->GetMinVoxelSizeMm()/2); if (auto renderWindowPart = this->GetRenderWindowPart()) renderWindowPart->RequestUpdate(); } else { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(fib); QString name("FiberBundle_"); name += m_ParentNode->GetName().c_str(); name += "_Streamline"; node->SetName(name.toStdString()); - node->SetFloatProperty("Fiber2DSliceThickness", m_Tracker->GetMinVoxelSize()/2); + node->SetFloatProperty("Fiber2DSliceThickness", params->GetMinVoxelSizeMm()/2); GetDataStorage()->Add(node, m_ParentNode); } } else { TrackerType::ItkDoubleImgType::Pointer outImg = m_Tracker->GetOutputProbabilityMap(); mitk::Image::Pointer img = mitk::Image::New(); img->InitializeByItk(outImg.GetPointer()); img->SetVolume(outImg->GetBufferPointer()); if (m_Controls->m_InteractiveBox->isChecked()) { if (m_InteractiveNode.IsNull()) { m_InteractiveNode = mitk::DataNode::New(); QString name("Interactive"); m_InteractiveNode->SetName(name.toStdString()); GetDataStorage()->Add(m_InteractiveNode); } m_InteractiveNode->SetData(img); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); lut->SetType(mitk::LookupTable::JET_TRANSPARENT); mitk::LookupTableProperty::Pointer lut_prop = mitk::LookupTableProperty::New(); lut_prop->SetLookupTable(lut); m_InteractiveNode->SetProperty("LookupTable", lut_prop); m_InteractiveNode->SetProperty("opacity", mitk::FloatProperty::New(0.5)); - m_InteractiveNode->SetFloatProperty("Fiber2DSliceThickness", m_Tracker->GetMinVoxelSize()/2); + m_InteractiveNode->SetFloatProperty("Fiber2DSliceThickness", params->GetMinVoxelSizeMm()/2); if (auto renderWindowPart = this->GetRenderWindowPart()) renderWindowPart->RequestUpdate(); } else { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(img); QString name("ProbabilityMap_"); name += m_ParentNode->GetName().c_str(); node->SetName(name.toStdString()); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); lut->SetType(mitk::LookupTable::JET_TRANSPARENT); mitk::LookupTableProperty::Pointer lut_prop = mitk::LookupTableProperty::New(); lut_prop->SetLookupTable(lut); node->SetProperty("LookupTable", lut_prop); node->SetProperty("opacity", mitk::FloatProperty::New(0.5)); GetDataStorage()->Add(node, m_ParentNode); } } if (m_InteractivePointSetNode.IsNotNull()) m_InteractivePointSetNode->SetProperty("color", mitk::ColorProperty::New(1,1,1)); StartStopTrackingGui(false); if (m_DeleteTrackingHandler) DeleteTrackingHandler(); UpdateGui(); } void QmitkStreamlineTrackingView::InteractiveSeedChanged(bool posChanged) { if(!CheckAndStoreLastParams(sender()) && !posChanged) return; if (m_ThreadIsRunning || !m_Visible) return; if (!posChanged && (!m_Controls->m_InteractiveBox->isChecked() || !m_Controls->m_ParamUpdateBox->isChecked()) ) return; std::srand(std::time(0)); m_SeedPoints.clear(); itk::Point world_pos = this->GetRenderWindowPart()->GetSelectedPosition(); m_SeedPoints.push_back(world_pos); float radius = m_Controls->m_SeedRadiusBox->value(); int num = m_Controls->m_NumSeedsBox->value(); mitk::PointSet::Pointer pointset = mitk::PointSet::New(); pointset->InsertPoint(0, world_pos); m_InteractivePointSetNode->SetProperty("pointsize", mitk::FloatProperty::New(radius*2)); m_InteractivePointSetNode->SetProperty("point 2D size", mitk::FloatProperty::New(radius*2)); m_InteractivePointSetNode->SetData(pointset); for (int i=1; i p; p[0] = rand()%1000-500; p[1] = rand()%1000-500; p[2] = rand()%1000-500; p.Normalize(); p *= radius; m_SeedPoints.push_back(world_pos+p); } m_InteractivePointSetNode->SetProperty("color", mitk::ColorProperty::New(1,0,0)); DoFiberTracking(); } bool QmitkStreamlineTrackingView::CheckAndStoreLastParams(QObject* obj) { if (obj!=nullptr) { std::string new_val = ""; if(qobject_cast(obj)!=nullptr) new_val = boost::lexical_cast(qobject_cast(obj)->value()); else if (qobject_cast(obj)!=nullptr) new_val = boost::lexical_cast(qobject_cast(obj)->value()); else return true; if (m_LastTractoParams.find(obj->objectName())==m_LastTractoParams.end()) { m_LastTractoParams[obj->objectName()] = new_val; return false; } else if (m_LastTractoParams.at(obj->objectName()) != new_val) { m_LastTractoParams[obj->objectName()] = new_val; return true; } else if (m_LastTractoParams.at(obj->objectName()) == new_val) return false; } return true; } void QmitkStreamlineTrackingView::OnParameterChanged() { UpdateGui(); if(!CheckAndStoreLastParams(sender())) return; if (m_Controls->m_InteractiveBox->isChecked() && m_Controls->m_ParamUpdateBox->isChecked()) DoFiberTracking(); } void QmitkStreamlineTrackingView::ToggleInteractive() { UpdateGui(); m_Controls->m_SeedsPerVoxelBox->setEnabled(!m_Controls->m_InteractiveBox->isChecked()); m_Controls->m_SeedsPerVoxelLabel->setEnabled(!m_Controls->m_InteractiveBox->isChecked()); m_Controls->m_SeedImageSelectionWidget->setEnabled(!m_Controls->m_InteractiveBox->isChecked()); m_Controls->label_6->setEnabled(!m_Controls->m_InteractiveBox->isChecked()); if ( m_Controls->m_InteractiveBox->isChecked() ) { if (m_FirstInteractiveRun) { QMessageBox::information(nullptr, "Information", "Place and move a spherical seed region anywhere in the image by left-clicking and dragging. If the seed region is colored red, tracking is in progress. If the seed region is colored white, tracking is finished.\nPlacing the seed region for the first time in a newly selected dataset might cause a short delay, since the tracker needs to be initialized."); m_FirstInteractiveRun = false; } QApplication::setOverrideCursor(Qt::PointingHandCursor); QApplication::processEvents(); m_InteractivePointSetNode = mitk::DataNode::New(); m_InteractivePointSetNode->SetProperty("color", mitk::ColorProperty::New(1,1,1)); m_InteractivePointSetNode->SetName("InteractiveSeedRegion"); mitk::PointSetShapeProperty::Pointer shape_prop = mitk::PointSetShapeProperty::New(); shape_prop->SetValue(mitk::PointSetShapeProperty::PointSetShape::CIRCLE); m_InteractivePointSetNode->SetProperty("Pointset.2D.shape", shape_prop); GetDataStorage()->Add(m_InteractivePointSetNode); m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } else { QApplication::restoreOverrideCursor(); QApplication::processEvents(); m_InteractiveNode = nullptr; m_InteractivePointSetNode = nullptr; m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); disconnect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } } void QmitkStreamlineTrackingView::Activated() { } void QmitkStreamlineTrackingView::Deactivated() { } void QmitkStreamlineTrackingView::Visible() { m_Visible = true; } void QmitkStreamlineTrackingView::Hidden() { m_Visible = false; m_Controls->m_InteractiveBox->setChecked(false); ToggleInteractive(); } void QmitkStreamlineTrackingView::OnSliceChanged() { InteractiveSeedChanged(true); } void QmitkStreamlineTrackingView::SetFocus() { } void QmitkStreamlineTrackingView::DeleteTrackingHandler() { if (!m_ThreadIsRunning && m_TrackingHandler != nullptr) { if (m_TrackingPriorHandler != nullptr) { delete m_TrackingPriorHandler; m_TrackingPriorHandler = nullptr; } delete m_TrackingHandler; m_TrackingHandler = nullptr; m_DeleteTrackingHandler = false; m_LastPrior = nullptr; } else if (m_ThreadIsRunning) { m_DeleteTrackingHandler = true; } } void QmitkStreamlineTrackingView::ForestSwitched() { DeleteTrackingHandler(); } void QmitkStreamlineTrackingView::OutputStyleSwitched() { if (m_InteractiveNode.IsNotNull()) GetDataStorage()->Remove(m_InteractiveNode); m_InteractiveNode = nullptr; } void QmitkStreamlineTrackingView::OnSelectionChanged( berry::IWorkbenchPart::Pointer , const QList& nodes ) { std::vector< mitk::DataNode::Pointer > last_nodes = m_InputImageNodes; m_InputImageNodes.clear(); m_AdditionalInputImages.clear(); bool retrack = false; for( auto node : nodes ) { if( node.IsNotNull() && dynamic_cast(node->GetData()) ) { if( dynamic_cast(node->GetData()) || dynamic_cast(node->GetData()) || dynamic_cast(node->GetData()) || dynamic_cast(node->GetData()) || mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(node->GetData()))) { m_InputImageNodes.push_back(node); retrack = true; } else { mitk::Image* img = dynamic_cast(node->GetData()); if (img!=nullptr && img->GetDimension()==3) m_AdditionalInputImages.push_back(dynamic_cast(node->GetData())); } } } // sometimes the OnSelectionChanged event is sent twice and actually no selection has changed for the first event. We need to catch that. if (last_nodes.size() == m_InputImageNodes.size()) { bool same_nodes = true; for (unsigned int i=0; im_TensorImageLabel->setText("select in data-manager"); m_Controls->m_fBox->setEnabled(false); m_Controls->m_fLabel->setEnabled(false); m_Controls->m_gBox->setEnabled(false); m_Controls->m_gLabel->setEnabled(false); m_Controls->m_FaImageSelectionWidget->setEnabled(true); m_Controls->mFaImageLabel->setEnabled(true); m_Controls->m_OdfCutoffBox->setEnabled(false); m_Controls->m_OdfCutoffLabel->setEnabled(false); m_Controls->m_SharpenOdfsBox->setEnabled(false); m_Controls->m_ForestSelectionWidget->setVisible(false); m_Controls->m_ForestLabel->setVisible(false); m_Controls->commandLinkButton->setEnabled(false); m_Controls->m_TrialsPerSeedBox->setEnabled(false); m_Controls->m_TrialsPerSeedLabel->setEnabled(false); m_Controls->m_TargetImageSelectionWidget->setEnabled(false); m_Controls->m_TargetImageLabel->setEnabled(false); + m_Controls->m_PeakJitterBox->setEnabled(false); if (m_Controls->m_InteractiveBox->isChecked()) { m_Controls->m_InteractiveSeedingFrame->setVisible(true); m_Controls->m_StaticSeedingFrame->setVisible(false); m_Controls->commandLinkButton_2->setVisible(false); m_Controls->commandLinkButton->setVisible(false); } else { m_Controls->m_InteractiveSeedingFrame->setVisible(false); m_Controls->m_StaticSeedingFrame->setVisible(true); m_Controls->commandLinkButton_2->setVisible(m_ThreadIsRunning); m_Controls->commandLinkButton->setVisible(!m_ThreadIsRunning); } if (m_Controls->m_EpConstraintsBox->currentIndex()>0) { m_Controls->m_TargetImageSelectionWidget->setEnabled(true); m_Controls->m_TargetImageLabel->setEnabled(true); } - // trials per seed are only important for probabilistic tractography + // stuff that is only important for probabilistic tractography if (m_Controls->m_ModeBox->currentIndex()==1) { m_Controls->m_TrialsPerSeedBox->setEnabled(true); m_Controls->m_TrialsPerSeedLabel->setEnabled(true); + m_Controls->m_PeakJitterBox->setEnabled(true); } if(!m_InputImageNodes.empty()) { if (m_InputImageNodes.size()>1) m_Controls->m_TensorImageLabel->setText( ( std::to_string(m_InputImageNodes.size()) + " images selected").c_str() ); else m_Controls->m_TensorImageLabel->setText(m_InputImageNodes.at(0)->GetName().c_str()); m_Controls->commandLinkButton->setEnabled(!m_Controls->m_InteractiveBox->isChecked() && !m_ThreadIsRunning); m_Controls->m_ScalarThresholdBox->setEnabled(true); m_Controls->m_FaThresholdLabel->setEnabled(true); if ( dynamic_cast(m_InputImageNodes.at(0)->GetData()) ) { m_Controls->m_fBox->setEnabled(true); m_Controls->m_fLabel->setEnabled(true); m_Controls->m_gBox->setEnabled(true); m_Controls->m_gLabel->setEnabled(true); } else if ( dynamic_cast(m_InputImageNodes.at(0)->GetData()) || dynamic_cast(m_InputImageNodes.at(0)->GetData())) { m_Controls->m_OdfCutoffBox->setEnabled(true); m_Controls->m_OdfCutoffLabel->setEnabled(true); m_Controls->m_SharpenOdfsBox->setEnabled(true); } else if ( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(m_InputImageNodes.at(0)->GetData())) ) { m_Controls->m_ForestSelectionWidget->setVisible(true); m_Controls->m_ForestLabel->setVisible(true); m_Controls->m_ScalarThresholdBox->setEnabled(false); m_Controls->m_FaThresholdLabel->setEnabled(false); } } } void QmitkStreamlineTrackingView::StartStopTrackingGui(bool start) { m_ThreadIsRunning = start; if (!m_Controls->m_InteractiveBox->isChecked()) { m_Controls->commandLinkButton_2->setVisible(start); m_Controls->commandLinkButton->setVisible(!start); m_Controls->m_InteractiveBox->setEnabled(!start); m_Controls->m_StatusTextBox->setVisible(start); } } void QmitkStreamlineTrackingView::DoFiberTracking() { + auto params = GetParametersFromGui(); + if (m_InputImageNodes.empty()) { QMessageBox::information(nullptr, "Information", "Please select an input image in the datamaneger (tensor, ODF, peak or dMRI image)!"); return; } if (m_ThreadIsRunning || !m_Visible) return; if (m_Controls->m_InteractiveBox->isChecked() && m_SeedPoints.empty()) return; StartStopTrackingGui(true); m_Tracker = TrackerType::New(); + if (params->m_EpConstraints == itk::StreamlineTrackingFilter::EndpointConstraints::NONE) + m_Tracker->SetTargetRegions(nullptr); + if( dynamic_cast(m_InputImageNodes.at(0)->GetData()) ) { if (m_Controls->m_ModeBox->currentIndex()==1) { if (m_InputImageNodes.size()>1) { QMessageBox::information(nullptr, "Information", "Probabilistic tensor tractography is only implemented for single-tensor mode!"); StartStopTrackingGui(false); return; } if (m_TrackingHandler==nullptr) { m_TrackingHandler = new mitk::TrackingHandlerOdf(); typedef itk::TensorImageToOdfImageFilter< float, float > FilterType; FilterType::Pointer filter = FilterType::New(); filter->SetInput( mitk::convert::GetItkTensorFromTensorImage(dynamic_cast(m_InputImageNodes.at(0)->GetData())) ); filter->Update(); dynamic_cast(m_TrackingHandler)->SetOdfImage(filter->GetOutput()); if (m_Controls->m_FaImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer itkImg = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast(m_Controls->m_FaImageSelectionWidget->GetSelectedNode()->GetData()), itkImg); dynamic_cast(m_TrackingHandler)->SetGfaImage(itkImg); } } - dynamic_cast(m_TrackingHandler)->SetGfaThreshold(static_cast(m_Controls->m_ScalarThresholdBox->value())); - dynamic_cast(m_TrackingHandler)->SetOdfThreshold(0); - dynamic_cast(m_TrackingHandler)->SetSharpenOdfs(true); dynamic_cast(m_TrackingHandler)->SetIsOdfFromTensor(true); } else { if (m_TrackingHandler==nullptr) { m_TrackingHandler = new mitk::TrackingHandlerTensor(); for (unsigned int i=0; i(m_TrackingHandler)->AddTensorImage(mitk::convert::GetItkTensorFromTensorImage(dynamic_cast(m_InputImageNodes.at(i)->GetData())).GetPointer()); if (m_Controls->m_FaImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer itkImg = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast(m_Controls->m_FaImageSelectionWidget->GetSelectedNode()->GetData()), itkImg); dynamic_cast(m_TrackingHandler)->SetFaImage(itkImg); } } - - dynamic_cast(m_TrackingHandler)->SetFaThreshold(static_cast(m_Controls->m_ScalarThresholdBox->value())); - dynamic_cast(m_TrackingHandler)->SetF(static_cast(m_Controls->m_fBox->value())); - dynamic_cast(m_TrackingHandler)->SetG(static_cast(m_Controls->m_gBox->value())); } } else if ( dynamic_cast(m_InputImageNodes.at(0)->GetData()) || dynamic_cast(m_InputImageNodes.at(0)->GetData())) { if (m_TrackingHandler==nullptr) { m_TrackingHandler = new mitk::TrackingHandlerOdf(); if (dynamic_cast(m_InputImageNodes.at(0)->GetData())) dynamic_cast(m_TrackingHandler)->SetOdfImage(mitk::convert::GetItkOdfFromShImage(dynamic_cast(m_InputImageNodes.at(0)->GetData()))); else dynamic_cast(m_TrackingHandler)->SetOdfImage(mitk::convert::GetItkOdfFromOdfImage(dynamic_cast(m_InputImageNodes.at(0)->GetData()))); if (m_Controls->m_FaImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer itkImg = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast(m_Controls->m_FaImageSelectionWidget->GetSelectedNode()->GetData()), itkImg); dynamic_cast(m_TrackingHandler)->SetGfaImage(itkImg); } } - - dynamic_cast(m_TrackingHandler)->SetGfaThreshold(static_cast(m_Controls->m_ScalarThresholdBox->value())); - dynamic_cast(m_TrackingHandler)->SetOdfThreshold(static_cast(m_Controls->m_OdfCutoffBox->value())); - dynamic_cast(m_TrackingHandler)->SetSharpenOdfs(m_Controls->m_SharpenOdfsBox->isChecked()); } else if ( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(m_InputImageNodes.at(0)->GetData())) ) { if ( m_Controls->m_ForestSelectionWidget->GetSelectedNode().IsNull() ) { QMessageBox::information(nullptr, "Information", "Not random forest for machine learning based tractography (raw dMRI tractography) selected. Did you accidentally select the raw diffusion-weighted image in the datamanager?"); StartStopTrackingGui(false); return; } if (m_TrackingHandler==nullptr) { mitk::TractographyForest::Pointer forest = dynamic_cast(m_Controls->m_ForestSelectionWidget->GetSelectedNode()->GetData()); mitk::Image::Pointer dwi = dynamic_cast(m_InputImageNodes.at(0)->GetData()); std::vector< std::vector< ItkFloatImageType::Pointer > > additionalFeatureImages; additionalFeatureImages.push_back(std::vector< ItkFloatImageType::Pointer >()); for (auto img : m_AdditionalInputImages) { ItkFloatImageType::Pointer itkimg = ItkFloatImageType::New(); mitk::CastToItkImage(img, itkimg); additionalFeatureImages.at(0).push_back(itkimg); } bool forest_valid = false; if (forest->GetNumFeatures()>=100) { - unsigned int num_previous_directions = static_cast((forest->GetNumFeatures() - (100 + additionalFeatureImages.at(0).size()))/3); + params->m_NumPreviousDirections = static_cast((forest->GetNumFeatures() - (100 + additionalFeatureImages.at(0).size()))/3); m_TrackingHandler = new mitk::TrackingHandlerRandomForest<6, 100>(); dynamic_cast*>(m_TrackingHandler)->AddDwi(dwi); dynamic_cast*>(m_TrackingHandler)->SetAdditionalFeatureImages(additionalFeatureImages); dynamic_cast*>(m_TrackingHandler)->SetForest(forest); - dynamic_cast*>(m_TrackingHandler)->SetNumPreviousDirections(num_previous_directions); forest_valid = dynamic_cast*>(m_TrackingHandler)->IsForestValid(); } else { - unsigned int num_previous_directions = static_cast((forest->GetNumFeatures() - (28 + additionalFeatureImages.at(0).size()))/3); + params->m_NumPreviousDirections = static_cast((forest->GetNumFeatures() - (28 + additionalFeatureImages.at(0).size()))/3); m_TrackingHandler = new mitk::TrackingHandlerRandomForest<6, 28>(); dynamic_cast*>(m_TrackingHandler)->AddDwi(dwi); dynamic_cast*>(m_TrackingHandler)->SetAdditionalFeatureImages(additionalFeatureImages); dynamic_cast*>(m_TrackingHandler)->SetForest(forest); - dynamic_cast*>(m_TrackingHandler)->SetNumPreviousDirections(num_previous_directions); forest_valid = dynamic_cast*>(m_TrackingHandler)->IsForestValid(); } if (!forest_valid) { QMessageBox::information(nullptr, "Information", "Random forest is invalid. The forest signatue does not match the parameters of TrackingHandlerRandomForest."); StartStopTrackingGui(false); return; } } } else { - if (m_Controls->m_ModeBox->currentIndex()==1) - { - QMessageBox::information(nullptr, "Information", "Probabilstic tractography is not implemented for peak images."); - StartStopTrackingGui(false); - return; - } if (m_TrackingHandler==nullptr) { m_TrackingHandler = new mitk::TrackingHandlerPeaks(); dynamic_cast(m_TrackingHandler)->SetPeakImage(mitk::convert::GetItkPeakFromPeakImage(dynamic_cast(m_InputImageNodes.at(0)->GetData()))); } - - dynamic_cast(m_TrackingHandler)->SetPeakThreshold(static_cast(m_Controls->m_ScalarThresholdBox->value())); - } - - m_TrackingHandler->SetFlipX(m_Controls->m_FlipXBox->isChecked()); - m_TrackingHandler->SetFlipY(m_Controls->m_FlipYBox->isChecked()); - m_TrackingHandler->SetFlipZ(m_Controls->m_FlipZBox->isChecked()); - m_TrackingHandler->SetInterpolate(m_Controls->m_InterpolationBox->isChecked()); - switch (m_Controls->m_ModeBox->currentIndex()) - { - case 0: - m_TrackingHandler->SetMode(mitk::TrackingDataHandler::MODE::DETERMINISTIC); - break; - case 1: - m_TrackingHandler->SetMode(mitk::TrackingDataHandler::MODE::PROBABILISTIC); - break; - default: - m_TrackingHandler->SetMode(mitk::TrackingDataHandler::MODE::DETERMINISTIC); } if (m_Controls->m_InteractiveBox->isChecked()) { m_Tracker->SetSeedPoints(m_SeedPoints); } else if (m_Controls->m_SeedImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast(m_Controls->m_SeedImageSelectionWidget->GetSelectedNode()->GetData()), mask); m_Tracker->SetSeedImage(mask); } if (m_Controls->m_MaskImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast(m_Controls->m_MaskImageSelectionWidget->GetSelectedNode()->GetData()), mask); m_Tracker->SetMaskImage(mask); } if (m_Controls->m_StopImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast(m_Controls->m_StopImageSelectionWidget->GetSelectedNode()->GetData()), mask); m_Tracker->SetStoppingRegions(mask); } if (m_Controls->m_TargetImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast(m_Controls->m_TargetImageSelectionWidget->GetSelectedNode()->GetData()), mask); m_Tracker->SetTargetRegions(mask); } if (m_Controls->m_PriorImageSelectionWidget->GetSelectedNode().IsNotNull()) { + auto prior_params = GetParametersFromGui(); if (m_LastPrior!=m_Controls->m_PriorImageSelectionWidget->GetSelectedNode() || m_TrackingPriorHandler==nullptr) { typedef mitk::ImageToItk< mitk::TrackingHandlerPeaks::PeakImgType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(dynamic_cast(m_Controls->m_PriorImageSelectionWidget->GetSelectedNode()->GetData())); caster->SetCopyMemFlag(true); caster->Update(); mitk::TrackingHandlerPeaks::PeakImgType::Pointer itkImg = caster->GetOutput(); m_TrackingPriorHandler = new mitk::TrackingHandlerPeaks(); dynamic_cast(m_TrackingPriorHandler)->SetPeakImage(itkImg); - dynamic_cast(m_TrackingPriorHandler)->SetPeakThreshold(0.0); m_LastPrior = m_Controls->m_PriorImageSelectionWidget->GetSelectedNode(); } - m_TrackingPriorHandler->SetInterpolate(m_Controls->m_InterpolationBox->isChecked()); - m_TrackingPriorHandler->SetMode(mitk::TrackingDataHandler::MODE::DETERMINISTIC); + prior_params->m_FlipX = m_Controls->m_PriorFlipXBox->isChecked(); + prior_params->m_FlipY = m_Controls->m_PriorFlipYBox->isChecked(); + prior_params->m_FlipZ = m_Controls->m_PriorFlipZBox->isChecked(); + m_TrackingPriorHandler->SetParameters(prior_params); m_Tracker->SetTrackingPriorHandler(m_TrackingPriorHandler); - m_Tracker->SetTrackingPriorWeight(m_Controls->m_PriorWeightBox->value()); - m_Tracker->SetTrackingPriorAsMask(m_Controls->m_PriorAsMaskBox->isChecked()); - m_Tracker->SetIntroduceDirectionsFromPrior(m_Controls->m_NewDirectionsFromPriorBox->isChecked()); } else if (m_Controls->m_PriorImageSelectionWidget->GetSelectedNode().IsNull()) m_Tracker->SetTrackingPriorHandler(nullptr); if (m_Controls->m_ExclusionImageSelectionWidget->GetSelectedNode().IsNotNull()) { ItkFloatImageType::Pointer mask = ItkFloatImageType::New(); mitk::CastToItkImage(dynamic_cast(m_Controls->m_ExclusionImageSelectionWidget->GetSelectedNode()->GetData()), mask); m_Tracker->SetExclusionRegions(mask); } - // Endpoint constraints - switch (m_Controls->m_EpConstraintsBox->currentIndex()) - { - case 0: - m_Tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::NONE); - m_Tracker->SetTargetRegions(nullptr); - break; - case 1: - m_Tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET); - break; - case 2: - m_Tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_TARGET_LABELDIFF); - break; - case 3: - m_Tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_SEED_AND_TARGET); - break; - case 4: - m_Tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::MIN_ONE_EP_IN_TARGET); - break; - case 5: - m_Tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::ONE_EP_IN_TARGET); - break; - case 6: - m_Tracker->SetEndpointConstraint(itk::StreamlineTrackingFilter::EndpointConstraints::NO_EP_IN_TARGET); - break; - } - - if (m_Tracker->GetEndpointConstraint()!=itk::StreamlineTrackingFilter::EndpointConstraints::NONE && m_Controls->m_TargetImageSelectionWidget->GetSelectedNode().IsNull()) + if (params->m_EpConstraints!=itk::StreamlineTrackingFilter::EndpointConstraints::NONE && m_Controls->m_TargetImageSelectionWidget->GetSelectedNode().IsNull()) { QMessageBox::information(nullptr, "Error", "Endpoint constraints are used but no target image is set!"); StartStopTrackingGui(false); return; } - else if (m_Tracker->GetEndpointConstraint()==itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_SEED_AND_TARGET + else if (params->m_EpConstraints==itk::StreamlineTrackingFilter::EndpointConstraints::EPS_IN_SEED_AND_TARGET && (m_Controls->m_SeedImageSelectionWidget->GetSelectedNode().IsNull()|| m_Controls->m_TargetImageSelectionWidget->GetSelectedNode().IsNull()) ) { QMessageBox::information(nullptr, "Error", "Endpoint constraint EPS_IN_SEED_AND_TARGET is used but no target or no seed image is set!"); StartStopTrackingGui(false); return; } - m_Tracker->SetInterpolateMasks(m_Controls->m_MaskInterpolationBox->isChecked()); - m_Tracker->SetVerbose(!m_Controls->m_InteractiveBox->isChecked()); - m_Tracker->SetSeedsPerVoxel(m_Controls->m_SeedsPerVoxelBox->value()); - m_Tracker->SetStepSize(m_Controls->m_StepSizeBox->value()); - m_Tracker->SetSamplingDistance(m_Controls->m_SamplingDistanceBox->value()); - m_Tracker->SetUseStopVotes(m_Controls->m_StopVotesBox->isChecked()); - m_Tracker->SetOnlyForwardSamples(m_Controls->m_FrontalSamplesBox->isChecked()); - m_Tracker->SetTrialsPerSeed(m_Controls->m_TrialsPerSeedBox->value()); - m_Tracker->SetMaxNumTracts(m_Controls->m_NumFibersBox->value()); - m_Tracker->SetNumberOfSamples(m_Controls->m_NumSamplesBox->value()); + m_Tracker->SetParameters(params); m_Tracker->SetTrackingHandler(m_TrackingHandler); - m_Tracker->SetLoopCheck(m_Controls->m_LoopCheckBox->value()); - m_Tracker->SetAngularThreshold(m_Controls->m_AngularThresholdBox->value()); - m_Tracker->SetMinTractLength(m_Controls->m_MinTractLengthBox->value()); - m_Tracker->SetUseOutputProbabilityMap(m_Controls->m_OutputProbMap->isChecked()); - m_Tracker->SetRandom(!m_Controls->m_FixSeedBox->isChecked()); + m_Tracker->SetVerbose(!m_Controls->m_InteractiveBox->isChecked()); m_ParentNode = m_InputImageNodes.at(0); m_TrackingThread.start(QThread::LowestPriority); } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.h b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.h index f2d3bcb433..96b5d49952 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingView.h @@ -1,154 +1,160 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkStreamlineTrackingView_h #define QmitkStreamlineTrackingView_h #include #include "ui_QmitkStreamlineTrackingViewControls.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include class QmitkStreamlineTrackingView; class QmitkStreamlineTrackingWorker : public QObject { Q_OBJECT public: QmitkStreamlineTrackingWorker(QmitkStreamlineTrackingView* view); public slots: void run(); private: QmitkStreamlineTrackingView* m_View; }; /*! \brief View for tensor based deterministic streamline fiber tracking. */ class QmitkStreamlineTrackingView : public QmitkAbstractView, public mitk::ILifecycleAwarePart { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; typedef itk::Image< unsigned int, 3 > ItkUintImgType; typedef itk::Image< unsigned char, 3 > ItkUCharImageType; typedef itk::Image< float, 3 > ItkFloatImageType; typedef itk::StreamlineTrackingFilter TrackerType; QmitkStreamlineTrackingView(); virtual ~QmitkStreamlineTrackingView(); virtual void CreateQtPartControl(QWidget *parent) override; /// /// Sets the focus to an internal widget. /// virtual void SetFocus() override; TrackerType::Pointer m_Tracker; QmitkStreamlineTrackingWorker m_TrackingWorker; QThread m_TrackingThread; virtual void Activated() override; virtual void Deactivated() override; virtual void Visible() override; virtual void Hidden() override; protected slots: void DoFiberTracking(); ///< start fiber tracking void UpdateGui(); void ToggleInteractive(); void DeleteTrackingHandler(); void OnParameterChanged(); void InteractiveSeedChanged(bool posChanged=false); void ForestSwitched(); void OutputStyleSwitched(); void AfterThread(); ///< update gui etc. after tracking has finished void BeforeThread(); ///< start timer etc. void TimerUpdate(); void StopTractography(); void OnSliceChanged(); + void SaveParameters(); + void LoadParameters(); protected: /// \brief called by QmitkAbstractView when DataManager's selection has changed virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList& nodes) override; Ui::QmitkStreamlineTrackingViewControls* m_Controls; protected slots: private: bool CheckAndStoreLastParams(QObject* obj); void StartStopTrackingGui(bool start); + std::shared_ptr< mitk::StreamlineTractographyParameters > GetParametersFromGui(); + void ParametersToGui(mitk::StreamlineTractographyParameters& params); std::vector< itk::Point > m_SeedPoints; mitk::DataNode::Pointer m_ParentNode; mitk::DataNode::Pointer m_InteractiveNode; mitk::DataNode::Pointer m_InteractivePointSetNode; std::vector< mitk::DataNode::Pointer > m_InputImageNodes; ///< input image nodes std::vector< mitk::Image::ConstPointer > m_AdditionalInputImages; bool m_FirstTensorProbRun; bool m_FirstInteractiveRun; mitk::TrackingDataHandler* m_TrackingHandler; bool m_ThreadIsRunning; QTimer* m_TrackingTimer; bool m_DeleteTrackingHandler; QmitkSliceNavigationListener m_SliceChangeListener; bool m_Visible; mitk::DataNode::Pointer m_LastPrior; mitk::TrackingDataHandler* m_TrackingPriorHandler; std::map< QString, std::string > m_LastTractoParams; + QString m_ParameterFile; }; #endif // _QMITKFIBERTRACKINGVIEW_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingViewControls.ui index e7ec1f72fc..4c040ada44 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/QmitkStreamlineTrackingViewControls.ui @@ -1,1511 +1,1698 @@ QmitkStreamlineTrackingViewControls 0 0 453 859 0 0 QmitkTemplate QCommandLinkButton:disabled { border: none; } QGroupBox { background-color: transparent; } 3 3 0 40 QFrame::NoFrame QFrame::Raised 0 15 0 0 6 15 true 0 0 true + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + Save parameters to json file + + + Save Parameters + + + + :/QmitkTractography/download.png:/QmitkTractography/download.png + + + + + + + true + + + Load parameters from json file + + + Load Parameters + + + + :/QmitkTractography/upload.png:/QmitkTractography/upload.png + + + + + + + + + + false + + + Start Tractography + + + + :/QmitkTractography/right.png:/QmitkTractography/right.png + + + + + + + true + + + Stop tractography and return all fibers reconstructed until now. + + + Stop Tractography + + + + :/QmitkTractography/stop.png:/QmitkTractography/stop.png + + + QFrame::NoFrame QFrame::Raised 0 0 0 0 Input Image. ODF, tensor and peak images are currently supported. Input Image: Input Image. ODF, tensor, peak, and, in case of ML tractography, raw diffusion-weighted images are currently supported. <html><head/><body><p><span style=" color:#ff0000;">select image in data-manager</span></p></body></html> true Tractography Forest: - - - - true - - - Stop tractography and return all fibers reconstructed until now. - - - Stop Tractography - - - - - - - false - - - Start Tractography - - - 0 0 0 0 75 true 0 0 0 421 254 Seeding Specify how, where and how many tractography seed points are placed. QFrame::NoFrame QFrame::Raised 0 0 0 0 QFrame::NoFrame QFrame::Raised 0 0 0 0 Number of seed points equally distributed around selected position. 1 9999999 50 - Radius: + Radius: Seedpoints are equally distributed within a sphere centered at the selected position with the specified radius (in mm). 2 50.000000000000000 0.100000000000000 2.000000000000000 Num. Seeds: true When checked, parameter changes cause instant retracking while in interactive mode. - Update on Parameter Change + Update on Parameter Change true QFrame::NoFrame QFrame::Raised 0 0 0 0 Try each seed N times until a valid streamline is obtained (only for probabilistic tractography). Minimum fiber length (in mm) 1 999 10 Trials Per Seed: Max. Num. Fibers: Tractography is stopped after the desired number of fibers is reached, even before all seed points are processed (-1 means no limit). -1 999999999 -1 QFrame::NoFrame QFrame::Raised 0 0 0 0 Seeds per Voxel: Seed Image: Number of seed points placed in each voxel. 1 9999999 true Dynamically pick a seed location by click into image. Enable Interactive Tractography Qt::Vertical 20 40 0 0 435 - 232 + 184 ROI Constraints Specify various ROI and mask images to constrain the tractography process. Mask Image: Select which fibers should be accepted or rejected based on the location of their endpoints. QComboBox::AdjustToMinimumContentsLength No Constraints on EP locations Both EPs in Target Image Both EPs in Target Image But Different Label One EP in Seed Image and One EP in Target Image At Least One EP in Target Image Exactly One EP in Target Image No EP in Target Image Endpoint Constraints: Stop ROI Image: Exclusion ROI Image: Target ROI Image: Qt::Vertical 20 40 0 - -132 + 0 421 - 366 + 428 Tractography Parameters Specify the behavior of the tractography at each streamline integration step (step size, deterministic/probabilistic, ...). - - + + - Angular threshold between two steps (in degree). Default: 90° * step_size + Minimum tract length in mm. Shorter fibers are discarded. - - -1 + + Minimum fiber length (in mm) + + + 1 - 90 + 999.000000000000000 - 1 + 1.000000000000000 - -1 - - - - - - - - - - FA/GFA Image: - - - - - - - - - - ODF Cutoff: + 20.000000000000000 - - + + - Always produce the same random numbers. + f parameter of tensor tractography. f=1 + g=0 means FACT (depending on the chosen interpolation). f=0 and g=1 means TEND (disable interpolation for this mode!). - + f: - - - - Qt::Vertical - - - - 20 - 40 - - - - f=1 + g=0 means FACT (depending on the chosen interpolation). f=0 and g=1 means TEND (disable interpolation for this mode!). 2 1.000000000000000 0.100000000000000 0.000000000000000 - - + + - Minimum tract length in mm. Shorter fibers are discarded. - - - Minimum fiber length (in mm) - - - 1 - - - 999.000000000000000 - - - 1.000000000000000 + - - 20.000000000000000 + + Sharpen ODFs: - - + + - Sharpen ODFs: + Fix Random Seed: - - + + - Threshold on peak magnitude, FA, GFA, ... + Maximum allowed angular SDTEV over 4 voxel lengths. Default: no loop check. - - 5 + + - - 1.000000000000000 + + -1 - - 0.100000000000000 + + 180 - 0.100000000000000 + -1 - - + + - g: + Angular Threshold: - - + + - Loop Check: + Max. Tract Length: + + + + + + + + + + Cutoff: If you are using dODF images as input, it is advisable to sharpen the ODFs (min-max normalize and raise to the power of 4). This is not necessary for CSD fODFs, since they are naturally much sharper. - - + + + + Angular threshold between two steps (in degree). Default: 90° * step_size + + + -1 + + + 90 + + + 1 + + + -1 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + f=1 + g=0 means FACT (depending on the chosen interpolation). f=0 and g=1 means TEND (disable interpolation for this mode!). 2 1.000000000000000 0.100000000000000 1.000000000000000 - - + + - Maximum allowed angular SDTEV over 4 voxel lengths. Default: no loop check. + Toggle between deterministic and probabilistic tractography. Some modes might not be available for all types of tractography. - + + + Deterministic + + + + + Probabilistic + + + + + + + - - -1 + + Mode: + + + + + + + Additional threshold on the ODF magnitude. This is useful in case of CSD fODF tractography. For fODFs a good default value is 0.1, for normalized dODFs, e.g. Q-ball ODFs, this threshold should be very low (0.00025) or 0. + + + 5 - 180 + 1.000000000000000 + + + 0.100000000000000 - -1 + 0.000250000000000 Min. Tract Length: - - + + - f parameter of tensor tractography. f=1 + g=0 means FACT (depending on the chosen interpolation). f=0 and g=1 means TEND (disable interpolation for this mode!). + - f: + FA/GFA Image: - - + + - Angular Threshold: + Loop Check: - - + + - Step size (in voxels) + Minimum tract length in mm. Shorter fibers are discarded. - - 2 + + Maximum fiber length (in mm) - - 0.010000000000000 + + 1 - 10.000000000000000 + 999.000000000000000 - 0.100000000000000 + 1.000000000000000 - 0.500000000000000 + 400.000000000000000 - - + + - Toggle between deterministic and probabilistic tractography. Some modes might not be available for all types of tractography. + Always produce the same random numbers. + + + - - - Deterministic - - - - - Probabilistic - - - - + + - Mode: + Step Size: - - + + - Step Size: + ODF Cutoff: - - + + + + Step size (in voxels) + + + 2 + + + 0.010000000000000 + + + 10.000000000000000 + + + 0.100000000000000 + + + 0.500000000000000 + + + + + - Cutoff: + g: - - + + + + + - Additional threshold on the ODF magnitude. This is useful in case of CSD fODF tractography. For fODFs a good default value is 0.1, for normalized dODFs, e.g. Q-ball ODFs, this threshold should be very low (0.00025) or 0. + Threshold on peak magnitude, FA, GFA, ... 5 1.000000000000000 0.100000000000000 - 0.000250000000000 + 0.100000000000000 - + - Fix Random Seed: + Peak Jitter: + + + + + + + Important for probabilistic peak tractography and peak prior. Actual jitter is drawn from a normal distribution with peak_jitter*fabs(direction_value) as standard deviation. + + + 3 + + + 1.000000000000000 + + + 0.100000000000000 + + + 0.010000000000000 0 0 435 - 232 + 184 Tractography Prior - - - - Restrict to Prior: - - - Weight: - - - - Peak Image: - - - Weighting factor between prior and data. 1.000000000000000 0.100000000000000 0.500000000000000 - - - - Restrict tractography to regions where the prior is valid. - + + - - - - true + Peak Image: - + Qt::Vertical 20 40 + + + + If unchecked, the prior cannot create directions where there are none in the data. + + + + + + true + + + New Directions from Prior: - - + + + + Restrict to Prior: + + + + + + + + - If unchecked, the prior cannot create directions where there are none in the data. + Restrict tractography to regions where the prior is valid. true - - + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + y + + + + + + + x + + + + + + + z + + + + + + + + + + Flip Directions: + + 0 0 435 - 232 + 184 Neighborhood Sampling Specify if and how information about the current streamline neighborhood should be used. Only neighborhood samples in front of the current streamline position are considered. Use Only Frontal Samples false If checked, the majority of sampling points has to place a stop-vote for the streamline to terminate. If not checked, all sampling positions have to vote for a streamline termination. Use Stop-Votes false QFrame::NoFrame QFrame::Raised 0 0 0 0 Num. Samples: Number of neighborhood samples that are used to determine the next fiber progression direction. 50 Sampling Distance: Sampling distance (in voxels) 2 10.000000000000000 0.100000000000000 0.250000000000000 Qt::Vertical 20 40 0 0 435 - 232 + 184 Data Handling Specify interpolation and direction flips. QFrame::NoFrame QFrame::Raised 0 0 0 0 Trilinearly interpolate the input image used for tractography. Interpolate Tractography Data true Trilinearly interpolate the ROI images used to constrain the tractography. Interpolate ROI Images true QFrame::NoFrame QFrame::Raised 0 0 0 0 QFrame::NoFrame QFrame::Raised 0 0 0 0 Internally flips progression directions. This might be necessary depending on the input data. x Internally flips progression directions. This might be necessary depending on the input data. y Internally flips progression directions. This might be necessary depending on the input data. z Flip directions: Qt::Vertical 20 40 0 0 435 - 232 + 184 Output and Postprocessing Specify the tractography output (streamlines or probability maps) and postprocessing steps. QFrame::NoFrame QFrame::Raised 0 0 0 0 Compress fibers using the specified error constraint. Compress Fibers true Qt::StrongFocus Lossy fiber compression. Recommended for large tractograms. Maximum error in mm. 3 10.000000000000000 0.010000000000000 0.100000000000000 Output map with voxel-wise visitation counts instead of streamlines. Output Probability Map false Qt::Vertical 20 40 QmitkSingleNodeSelectionWidget QWidget
QmitkSingleNodeSelectionWidget.h
1
- + + +
diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/mitkPluginActivator.cpp index d5ad628635..9925065c85 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/mitkPluginActivator.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.tractography/src/internal/mitkPluginActivator.cpp @@ -1,49 +1,47 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkPluginActivator.h" #include "src/internal/Perspectives/QmitkGibbsTractographyPerspective.h" #include "src/internal/Perspectives/QmitkStreamlineTractographyPerspective.h" #include "src/internal/QmitkGibbsTrackingView.h" #include "src/internal/QmitkStreamlineTrackingView.h" -#include "src/internal/QmitkMLBTView.h" ctkPluginContext* mitk::PluginActivator::m_Context = nullptr; ctkPluginContext* mitk::PluginActivator::GetContext() { return m_Context; } void mitk::PluginActivator::start(ctkPluginContext* context) { BERRY_REGISTER_EXTENSION_CLASS(QmitkGibbsTractographyPerspective, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkStreamlineTractographyPerspective, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkGibbsTrackingView, context) BERRY_REGISTER_EXTENSION_CLASS(QmitkStreamlineTrackingView, context) - BERRY_REGISTER_EXTENSION_CLASS(QmitkMLBTView, context) m_Context = context; } void mitk::PluginActivator::stop(ctkPluginContext* context) { Q_UNUSED(context) m_Context = nullptr; } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc index 164aaf9ed7..e114ea049d 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/QmitkDiffusionImaging.qrc @@ -1,33 +1,31 @@ dwi.png dwiimport.png texIntONIcon.png texIntOFFIcon.png vizControls.png Refresh_48.png glyphsoff_C.png glyphsoff_S.png glyphsoff_T.png glyphson_C.png glyphson_S.png glyphson_T.png - color24.gif - color48.gif - color64.gif crosshair.png paint2.png reset.png MapperEfx2D.png refresh.xpm tractogram.png ml_tractogram.png ConnectomicsNetwork.png odf.png DiffData24.png odf_peaks.png segmentation.svg sh.png tensor.png + color64.png diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color24.gif b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color24.gif deleted file mode 100644 index 07699c0173..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color24.gif and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color48.gif b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color48.gif deleted file mode 100644 index d206296c70..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color48.gif and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color64.gif b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color64.gif deleted file mode 100644 index be8b1bddcd..0000000000 Binary files a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color64.gif and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color64.png b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color64.png new file mode 100644 index 0000000000..04d4d1ae5f Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimaging/resources/color64.png differ diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp index c5f8b976e6..78d2287b87 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesView.cpp @@ -1,1247 +1,1250 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkControlVisualizationPropertiesView.h" #include "mitkNodePredicateDataType.h" #include "mitkDataNodeObject.h" #include "mitkOdfNormalizationMethodProperty.h" #include "mitkOdfScaleByProperty.h" #include "mitkResliceMethodProperty.h" #include "mitkRenderingManager.h" #include "mitkImageCast.h" #include "mitkShImage.h" #include "mitkPlanarFigure.h" #include "mitkFiberBundle.h" #include "QmitkDataStorageComboBox.h" #include "mitkPlanarFigureInteractor.h" #include #include #include #include #include #include "usModuleRegistry.h" #include #include #include "mitkPlaneGeometry.h" #include #include #include #include #include "berryIWorkbenchWindow.h" #include "berryIWorkbenchPage.h" #include "berryISelectionService.h" #include "berryConstants.h" #include "berryPlatformUI.h" #include "itkRGBAPixel.h" #include #include "qwidgetaction.h" #include "qcolordialog.h" #include #include #include #include #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) const std::string QmitkControlVisualizationPropertiesView::VIEW_ID = "org.mitk.views.controlvisualizationpropertiesview"; using namespace berry; QmitkControlVisualizationPropertiesView::QmitkControlVisualizationPropertiesView() : QmitkAbstractView(), m_Controls(nullptr), m_CurrentSelection(nullptr), m_IconTexOFF(new QIcon(":/QmitkDiffusionImaging/texIntOFFIcon.png")), m_IconTexON(new QIcon(":/QmitkDiffusionImaging/texIntONIcon.png")), m_IconGlyOFF_T(new QIcon(":/QmitkDiffusionImaging/glyphsoff_T.png")), m_IconGlyON_T(new QIcon(":/QmitkDiffusionImaging/glyphson_T.png")), m_IconGlyOFF_C(new QIcon(":/QmitkDiffusionImaging/glyphsoff_C.png")), m_IconGlyON_C(new QIcon(":/QmitkDiffusionImaging/glyphson_C.png")), m_IconGlyOFF_S(new QIcon(":/QmitkDiffusionImaging/glyphsoff_S.png")), m_IconGlyON_S(new QIcon(":/QmitkDiffusionImaging/glyphson_S.png")), m_GlyIsOn_T(false), m_GlyIsOn_C(false), m_GlyIsOn_S(false), m_CurrentThickSlicesMode(1), m_CurrentThickSlicesNum(0), m_CurrentPickingNode(nullptr), m_ColorPropertyObserverTag(0), m_OpacityPropertyObserverTag(0) { m_MyMenu = nullptr; auto numThread = itk::MultiThreader::GetGlobalMaximumNumberOfThreads(); itk::MultiThreader::SetGlobalDefaultNumberOfThreads(numThread); } QmitkControlVisualizationPropertiesView::~QmitkControlVisualizationPropertiesView() { } void QmitkControlVisualizationPropertiesView::SetTs(int currentThickSlicesMode, int num, std::string render_window) { if (auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN)) { mitk::BaseRenderer::Pointer renderer = renderWindowPart->GetQmitkRenderWindow(QString(render_window.c_str()))->GetRenderer(); renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(num)); if(num>0) { renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New(currentThickSlicesMode)); renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices.showarea", mitk::BoolProperty::New(true)); } else { renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New(0)); renderer->GetCurrentWorldPlaneGeometryNode()->SetProperty("reslice.thickslices.showarea", mitk::BoolProperty::New(false)); } renderer->SendUpdateSlice(); renderer->GetRenderingManager()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::OnThickSlicesModeSelected( QAction* action ) { m_CurrentThickSlicesMode = action->data().toInt(); switch( m_CurrentThickSlicesMode ) { case 0: return; case 1: this->m_Controls->m_TSMenu->setText("MIP"); break; case 2: this->m_Controls->m_TSMenu->setText("SUM"); break; case 3: this->m_Controls->m_TSMenu->setText("WEIGH"); break; default: return; } SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "axial"); SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "sagittal"); SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "coronal"); } void QmitkControlVisualizationPropertiesView::OnTSNumChanged( int num ) { m_CurrentThickSlicesNum = num; SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "axial"); SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "sagittal"); SetTs(m_CurrentThickSlicesMode, m_CurrentThickSlicesNum, "coronal"); m_TSLabel->setText(QString::number( num*2 + 1 )); } void QmitkControlVisualizationPropertiesView::CreateQtPartControl(QWidget *parent) { if (!m_Controls) { // create GUI widgets m_Controls = new Ui::QmitkControlVisualizationPropertiesViewControls; m_Controls->setupUi(parent); this->CreateConnections(); // hide warning (ODFs in rotated planes) m_Controls->m_lblRotatedPlanesWarning->hide(); m_MyMenu = new QMenu(parent); m_Controls->m_TSMenu->setMenu( m_MyMenu ); QIcon iconFiberFade(":/QmitkDiffusionImaging/MapperEfx2D.png"); m_Controls->m_FiberFading2D->setIcon(iconFiberFade); m_Controls->m_NormalizationFrame->setVisible(false); m_Controls->m_Crosshair->setVisible(false); mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow) { m_SliceChangeListener.RenderWindowPartActivated(renderWindow); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } connect(m_Controls->m_SetColor1, SIGNAL(clicked()), this, SLOT(SetColor())); connect(m_Controls->m_SetColor2, SIGNAL(clicked()), this, SLOT(SetColor())); } } void QmitkControlVisualizationPropertiesView::SetColor() { if(m_SelectedNode) { QColor c = QColorDialog::getColor(); - float rgb[3]; - rgb[0] = static_cast(c.redF()); - rgb[1] = static_cast(c.greenF()); - rgb[2] = static_cast(c.blueF()); - m_SelectedNode->SetColor(rgb); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + if (c.isValid()) + { + float rgb[3]; + rgb[0] = static_cast(c.redF()); + rgb[1] = static_cast(c.greenF()); + rgb[2] = static_cast(c.blueF()); + m_SelectedNode->SetColor(rgb); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } } } void QmitkControlVisualizationPropertiesView::SetFocus() { m_Controls->m_TSMenu->setFocus(); } void QmitkControlVisualizationPropertiesView::SliceRotation(const itk::EventObject&) { // test if plane rotated if( m_GlyIsOn_T || m_GlyIsOn_C || m_GlyIsOn_S ) { if( this->IsPlaneRotated() ) { // show label m_Controls->m_lblRotatedPlanesWarning->show(); } else { //hide label m_Controls->m_lblRotatedPlanesWarning->hide(); } } } void QmitkControlVisualizationPropertiesView::NodeRemoved(const mitk::DataNode* /*node*/) { } #include void QmitkControlVisualizationPropertiesView::CreateConnections() { if ( m_Controls ) { connect( static_cast(m_Controls->m_VisibleOdfsON_T), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_T()) ); connect( static_cast(m_Controls->m_VisibleOdfsON_S), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_S()) ); connect( static_cast(m_Controls->m_VisibleOdfsON_C), SIGNAL(clicked()), this, SLOT(VisibleOdfsON_C()) ); connect( static_cast(m_Controls->m_ShowMaxNumber), SIGNAL(editingFinished()), this, SLOT(ShowMaxNumberChanged()) ); connect( static_cast(m_Controls->m_NormalizationDropdown), SIGNAL(currentIndexChanged(int)), this, SLOT(NormalizationDropdownChanged(int)) ); connect( static_cast(m_Controls->m_ScalingFactor), SIGNAL(valueChanged(double)), this, SLOT(ScalingFactorChanged(double)) ); connect( static_cast(m_Controls->m_AdditionalScaling), SIGNAL(currentIndexChanged(int)), this, SLOT(AdditionalScaling(int)) ); connect(static_cast(m_Controls->m_ResetColoring), SIGNAL(clicked()), static_cast(this), SLOT(ResetColoring())); connect(static_cast(m_Controls->m_ResetColoring2), SIGNAL(clicked()), static_cast(this), SLOT(ResetColoring())); connect(static_cast(m_Controls->m_FiberFading2D), SIGNAL(clicked()), static_cast(this), SLOT( Fiber2DfadingEFX() ) ); connect(static_cast(m_Controls->m_FiberThicknessSlider), SIGNAL(sliderReleased()), static_cast(this), SLOT( FiberSlicingThickness2D() ) ); connect(static_cast(m_Controls->m_FiberThicknessSlider), SIGNAL(valueChanged(int)), static_cast(this), SLOT( FiberSlicingUpdateLabel(int) )); connect(static_cast(m_Controls->m_Crosshair), SIGNAL(clicked()), static_cast(this), SLOT(SetInteractor())); connect(static_cast(m_Controls->m_LineWidth), SIGNAL(editingFinished()), static_cast(this), SLOT(LineWidthChanged())); connect(static_cast(m_Controls->m_TubeWidth), SIGNAL(editingFinished()), static_cast(this), SLOT(TubeRadiusChanged())); connect(static_cast(m_Controls->m_RibbonWidth), SIGNAL(editingFinished()), static_cast(this), SLOT(RibbonWidthChanged())); connect( static_cast(m_Controls->m_OdfColorBox), SIGNAL(currentIndexChanged(int)), static_cast(this), SLOT(OnColourisationModeChanged() ) ); connect(static_cast(m_Controls->m_Clip0), SIGNAL(toggled(bool)), static_cast(this), SLOT(Toggle3DClipping(bool))); connect(static_cast(m_Controls->m_Clip1), SIGNAL(toggled(bool)), static_cast(this), SLOT(Toggle3DClipping(bool))); connect(static_cast(m_Controls->m_Clip2), SIGNAL(toggled(bool)), static_cast(this), SLOT(Toggle3DClipping(bool))); connect(static_cast(m_Controls->m_Clip3), SIGNAL(toggled(bool)), static_cast(this), SLOT(Toggle3DClipping(bool))); connect(static_cast(m_Controls->m_FlipClipBox), SIGNAL(stateChanged(int)), static_cast(this), SLOT(Toggle3DClipping())); connect(static_cast(m_Controls->m_Enable3dPeaks), SIGNAL(stateChanged(int)), static_cast(this), SLOT(Toggle3DPeaks())); connect(static_cast(m_Controls->m_FlipPeaksButton), SIGNAL(clicked()), static_cast(this), SLOT(FlipPeaks())); m_Controls->m_BundleControlsFrame->setVisible(false); m_Controls->m_ImageControlsFrame->setVisible(false); m_Controls->m_PeakImageFrame->setVisible(false); m_Controls->m_lblRotatedPlanesWarning->setVisible(false); m_Controls->m_3DClippingBox->setVisible(false); } } // set diffusion image channel to b0 volume void QmitkControlVisualizationPropertiesView::NodeAdded(const mitk::DataNode *node) { mitk::DataNode* notConst = const_cast(node); bool isDiffusionImage( mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage( dynamic_cast(node->GetData())) ); if (isDiffusionImage) { mitk::Image::Pointer dimg = dynamic_cast(notConst->GetData()); // if there is no b0 image in the dataset, the GetB0Indices() returns a vector of size 0 // and hence we cannot set the Property directly to .front() int displayChannelPropertyValue = 0; mitk::DiffusionPropertyHelper::BValueMapType map = mitk::DiffusionPropertyHelper::GetBValueMap(dimg); if( map[0].size() > 0) { displayChannelPropertyValue = map[0].front(); } notConst->SetIntProperty("DisplayChannel", displayChannelPropertyValue ); } } /* OnSelectionChanged is registered to SelectionService, therefore no need to implement SelectionService Listener explicitly */ void QmitkControlVisualizationPropertiesView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& nodes) { m_Controls->m_BundleControlsFrame->setVisible(false); m_Controls->m_ImageControlsFrame->setVisible(false); m_Controls->m_PeakImageFrame->setVisible(false); m_Controls->m_3DClippingBox->setVisible(false); m_Controls->m_FlipClipBox->setVisible(false); m_Controls->m_Enable3dPeaks->setVisible(false); if (nodes.size()>1) // only do stuff if one node is selected return; m_Controls->m_NumberGlyphsFrame->setVisible(false); m_Controls->m_GlyphFrame->setVisible(false); m_Controls->m_TSMenu->setVisible(false); m_SelectedNode = nullptr; int numOdfImages = 0; for (mitk::DataNode::Pointer node: nodes) { if(node.IsNull()) continue; mitk::BaseData* nodeData = node->GetData(); if(nodeData == nullptr) continue; m_SelectedNode = node; if (dynamic_cast(nodeData)) { m_Controls->m_PeakImageFrame->setVisible(true); if (m_Color.IsNotNull()) m_Color->RemoveObserver(m_ColorPropertyObserverTag); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkControlVisualizationPropertiesView::SetCustomColor ); m_Color = dynamic_cast(node->GetProperty("color", nullptr)); if (m_Color.IsNotNull()) m_ColorPropertyObserverTag = m_Color->AddObserver( itk::ModifiedEvent(), command ); int ClippingPlaneId = -1; m_SelectedNode->GetPropertyValue("3DClippingPlaneId",ClippingPlaneId); switch(ClippingPlaneId) { case 0: m_Controls->m_Clip0->setChecked(1); break; case 1: m_Controls->m_Clip1->setChecked(1); break; case 2: m_Controls->m_Clip2->setChecked(1); break; case 3: m_Controls->m_Clip3->setChecked(1); break; default : m_Controls->m_Clip0->setChecked(1); } m_Controls->m_Enable3dPeaks->setVisible(true); m_Controls->m_3DClippingBox->setVisible(true); } else if (dynamic_cast(nodeData)) { int ClippingPlaneId = -1; m_SelectedNode->GetPropertyValue("3DClippingPlaneId",ClippingPlaneId); switch(ClippingPlaneId) { case 0: m_Controls->m_Clip0->setChecked(1); break; case 1: m_Controls->m_Clip1->setChecked(1); break; case 2: m_Controls->m_Clip2->setChecked(1); break; case 3: m_Controls->m_Clip3->setChecked(1); break; default : m_Controls->m_Clip0->setChecked(1); } // handle fiber property observers if (m_Color.IsNotNull()) m_Color->RemoveObserver(m_ColorPropertyObserverTag); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkControlVisualizationPropertiesView::SetCustomColor ); m_Color = dynamic_cast(node->GetProperty("color", nullptr)); if (m_Color.IsNotNull()) m_ColorPropertyObserverTag = m_Color->AddObserver( itk::ModifiedEvent(), command ); m_Controls->m_FlipClipBox->setVisible(true); m_Controls->m_3DClippingBox->setVisible(true); m_Controls->m_BundleControlsFrame->setVisible(true); if(m_CurrentPickingNode != 0 && node.GetPointer() != m_CurrentPickingNode) { m_Controls->m_Crosshair->setEnabled(false); } else { m_Controls->m_Crosshair->setEnabled(true); } int width; node->GetIntProperty("shape.linewidth", width); m_Controls->m_LineWidth->setValue(width); float radius; node->GetFloatProperty("shape.tuberadius", radius); m_Controls->m_TubeWidth->setValue(radius); float range; node->GetFloatProperty("Fiber2DSliceThickness",range); mitk::FiberBundle::Pointer fib = dynamic_cast(node->GetData()); mitk::BaseGeometry::Pointer geo = fib->GetGeometry(); mitk::ScalarType max = geo->GetExtentInMM(0); max = std::max(max, geo->GetExtentInMM(1)); max = std::max(max, geo->GetExtentInMM(2)); m_Controls->m_FiberThicknessSlider->setMaximum(max * 10); m_Controls->m_FiberThicknessSlider->setValue(range * 10); } else if(dynamic_cast(nodeData) || dynamic_cast(nodeData) || dynamic_cast(nodeData)) { m_Controls->m_ImageControlsFrame->setVisible(true); m_Controls->m_NumberGlyphsFrame->setVisible(true); m_Controls->m_GlyphFrame->setVisible(true); m_Controls->m_NormalizationFrame->setVisible(true); if(m_NodeUsedForOdfVisualization.IsNotNull()) { m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", false); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", false); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", false); } m_NodeUsedForOdfVisualization = node; m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", m_GlyIsOn_S); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", m_GlyIsOn_C); m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", m_GlyIsOn_T); if (dynamic_cast(nodeData)) { m_Controls->m_NormalizationDropdown->setVisible(false); m_Controls->m_NormalizationLabel->setVisible(false); } else { m_Controls->m_NormalizationDropdown->setVisible(true); m_Controls->m_NormalizationLabel->setVisible(true); } int val; node->GetIntProperty("ShowMaxNumber", val); m_Controls->m_ShowMaxNumber->setValue(val); m_Controls->m_NormalizationDropdown->setCurrentIndex(dynamic_cast(node->GetProperty("Normalization"))->GetValueAsId()); float fval; node->GetFloatProperty("Scaling",fval); m_Controls->m_ScalingFactor->setValue(fval); m_Controls->m_AdditionalScaling->setCurrentIndex(dynamic_cast(node->GetProperty("ScaleBy"))->GetValueAsId()); bool switchTensorViewValue = false; node->GetBoolProperty( "DiffusionCore.Rendering.OdfVtkMapper.SwitchTensorView", switchTensorViewValue ); bool colourisationModeBit = false; node->GetBoolProperty("DiffusionCore.Rendering.OdfVtkMapper.ColourisationModeBit", colourisationModeBit ); m_Controls->m_OdfColorBox->setCurrentIndex(colourisationModeBit); numOdfImages++; } else if(dynamic_cast(nodeData)) { PlanarFigureFocus(); } else if( dynamic_cast(nodeData) ) { m_Controls->m_ImageControlsFrame->setVisible(true); m_Controls->m_TSMenu->setVisible(true); } } if( nodes.empty() ) { return; } mitk::DataNode::Pointer node = nodes.at(0); if( node.IsNull() ) { return; } QMenu *myMenu = m_MyMenu; myMenu->clear(); QActionGroup* thickSlicesActionGroup = new QActionGroup(myMenu); thickSlicesActionGroup->setExclusive(true); int currentTSMode = 0; { mitk::ResliceMethodProperty::Pointer m = dynamic_cast(node->GetProperty( "reslice.thickslices" )); if( m.IsNotNull() ) currentTSMode = m->GetValueAsId(); } int maxTS = 30; for (auto node: nodes) { mitk::Image* image = dynamic_cast(node->GetData()); if (image) { int size = std::max(image->GetDimension(0), std::max(image->GetDimension(1), image->GetDimension(2))); if (size>maxTS) { maxTS=size; } } } maxTS /= 2; int currentNum = 0; { mitk::IntProperty::Pointer m = dynamic_cast(node->GetProperty( "reslice.thickslices.num" )); if( m.IsNotNull() ) { currentNum = m->GetValue(); if(currentNum < 0) { currentNum = 0; } if(currentNum > maxTS) { currentNum = maxTS; } } } if(currentTSMode==0) { currentNum=0; } QSlider *m_TSSlider = new QSlider(myMenu); m_TSSlider->setMinimum(0); m_TSSlider->setMaximum(maxTS-1); m_TSSlider->setValue(currentNum); m_TSSlider->setOrientation(Qt::Horizontal); connect( m_TSSlider, SIGNAL( valueChanged(int) ), this, SLOT( OnTSNumChanged(int) ) ); QHBoxLayout* _TSLayout = new QHBoxLayout; _TSLayout->setContentsMargins(4,4,4,4); _TSLayout->addWidget(m_TSSlider); _TSLayout->addWidget(m_TSLabel=new QLabel(QString::number(currentNum*2+1),myMenu)); QWidget* _TSWidget = new QWidget; _TSWidget->setLayout(_TSLayout); QActionGroup* thickSliceModeActionGroup = new QActionGroup(myMenu); thickSliceModeActionGroup->setExclusive(true); QWidgetAction *m_TSSliderAction = new QWidgetAction(myMenu); m_TSSliderAction->setDefaultWidget(_TSWidget); myMenu->addAction(m_TSSliderAction); QAction* mipThickSlicesAction = new QAction(myMenu); mipThickSlicesAction->setActionGroup(thickSliceModeActionGroup); mipThickSlicesAction->setText("MIP (max. intensity proj.)"); mipThickSlicesAction->setCheckable(true); mipThickSlicesAction->setChecked(m_CurrentThickSlicesMode==1); mipThickSlicesAction->setData(1); myMenu->addAction( mipThickSlicesAction ); QAction* sumThickSlicesAction = new QAction(myMenu); sumThickSlicesAction->setActionGroup(thickSliceModeActionGroup); sumThickSlicesAction->setText("SUM (sum intensity proj.)"); sumThickSlicesAction->setCheckable(true); sumThickSlicesAction->setChecked(m_CurrentThickSlicesMode==2); sumThickSlicesAction->setData(2); myMenu->addAction( sumThickSlicesAction ); QAction* weightedThickSlicesAction = new QAction(myMenu); weightedThickSlicesAction->setActionGroup(thickSliceModeActionGroup); weightedThickSlicesAction->setText("WEIGHTED (gaussian proj.)"); weightedThickSlicesAction->setCheckable(true); weightedThickSlicesAction->setChecked(m_CurrentThickSlicesMode==3); weightedThickSlicesAction->setData(3); myMenu->addAction( weightedThickSlicesAction ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); connect( thickSliceModeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(OnThickSlicesModeSelected(QAction*)) ); } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_S() { m_GlyIsOn_S = m_Controls->m_VisibleOdfsON_S->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is nullptr"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_S", m_GlyIsOn_S); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Visible() { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow) { m_SliceChangeListener.RenderWindowPartActivated(renderWindow); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } } void QmitkControlVisualizationPropertiesView::Hidden() { } void QmitkControlVisualizationPropertiesView::Activated() { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow) { m_SliceChangeListener.RenderWindowPartActivated(renderWindow); connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); } } void QmitkControlVisualizationPropertiesView::Deactivated() { } void QmitkControlVisualizationPropertiesView::FlipPeaks() { if (m_SelectedNode.IsNull() || dynamic_cast(m_SelectedNode->GetData())==nullptr) return; std::string name = m_SelectedNode->GetName(); mitk::Image::Pointer image = dynamic_cast(m_SelectedNode->GetData()); typedef mitk::ImageToItk< mitk::PeakImage::ItkPeakImageType > CasterType; CasterType::Pointer caster = CasterType::New(); caster->SetInput(image); caster->Update(); mitk::PeakImage::ItkPeakImageType::Pointer itkImg = caster->GetOutput(); itk::FlipPeaksFilter< float >::Pointer flipper = itk::FlipPeaksFilter< float >::New(); flipper->SetInput(itkImg); flipper->SetFlipX(m_Controls->m_FlipPeaksX->isChecked()); flipper->SetFlipY(m_Controls->m_FlipPeaksY->isChecked()); flipper->SetFlipZ(m_Controls->m_FlipPeaksZ->isChecked()); flipper->Update(); mitk::Image::Pointer resultImage = dynamic_cast(mitk::PeakImage::New().GetPointer()); mitk::CastToMitkImage(flipper->GetOutput(), resultImage); resultImage->SetVolume(flipper->GetOutput()->GetBufferPointer()); m_SelectedNode->SetData(resultImage); m_SelectedNode->SetName(name); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Toggle3DPeaks() { if (m_SelectedNode.IsNull() || dynamic_cast(m_SelectedNode->GetData())==nullptr) return; bool enabled = false; m_SelectedNode->GetBoolProperty("Enable3DPeaks", enabled); m_SelectedNode->SetBoolProperty( "Enable3DPeaks", !enabled ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Toggle3DClipping(bool enabled) { if (!enabled || m_SelectedNode.IsNull() || (dynamic_cast(m_SelectedNode->GetData())==nullptr && dynamic_cast(m_SelectedNode->GetData())==nullptr)) return; m_SelectedNode->SetBoolProperty( "3DClippingPlaneFlip", m_Controls->m_FlipClipBox->isChecked() ); if (m_Controls->m_Clip0->isChecked()) { m_SelectedNode->SetIntProperty( "3DClippingPlaneId", 0 ); Set3DClippingPlane(true, m_SelectedNode, ""); } else if (m_Controls->m_Clip1->isChecked()) { m_SelectedNode->SetIntProperty( "3DClippingPlaneId", 1 ); Set3DClippingPlane(false, m_SelectedNode, "axial"); } else if (m_Controls->m_Clip2->isChecked()) { m_SelectedNode->SetIntProperty( "3DClippingPlaneId", 2 ); Set3DClippingPlane(false, m_SelectedNode, "sagittal"); } else if (m_Controls->m_Clip3->isChecked()) { m_SelectedNode->SetIntProperty( "3DClippingPlaneId", 3 ); Set3DClippingPlane(false, m_SelectedNode, "coronal"); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::OnSliceChanged() { mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->GetDataStorage()->GetAll(); for (unsigned int i=0; iSize(); ++i) { mitk::DataNode::Pointer node = nodes->GetElement(i); int plane_id = -1; node->GetIntProperty("3DClippingPlaneId", plane_id); if (plane_id==1) Set3DClippingPlane(false, node, "axial"); else if (plane_id==2) Set3DClippingPlane(false, node, "sagittal"); else if (plane_id==3) Set3DClippingPlane(false, node, "coronal"); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Set3DClippingPlane(bool disable, mitk::DataNode* node, std::string plane) { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow && node && (dynamic_cast(node->GetData()) || dynamic_cast(node->GetData()))) { mitk::Vector3D planeNormal; planeNormal.Fill(0.0); mitk::Point3D planeOrigin; planeOrigin.Fill(0.0); if (!disable) { mitk::SliceNavigationController* slicer = renderWindow->GetQmitkRenderWindow(QString(plane.c_str()))->GetSliceNavigationController(); mitk::PlaneGeometry::ConstPointer planeGeo = slicer->GetCurrentPlaneGeometry(); planeOrigin = this->GetRenderWindowPart()->GetSelectedPosition(); planeNormal = planeGeo->GetNormal(); } node->SetProperty( "3DClipping", mitk::ClippingProperty::New( planeOrigin, planeNormal ) ); if (dynamic_cast(node->GetData())) dynamic_cast(node->GetData())->RequestUpdate(); else mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_T() { m_GlyIsOn_T = m_Controls->m_VisibleOdfsON_T->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is nullptr"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_T", m_GlyIsOn_T); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::VisibleOdfsON_C() { m_GlyIsOn_C = m_Controls->m_VisibleOdfsON_C->isChecked(); if (m_NodeUsedForOdfVisualization.IsNull()) { MITK_WARN << "ODF visualization activated but m_NodeUsedForOdfVisualization is nullptr"; return; } m_NodeUsedForOdfVisualization->SetBoolProperty("VisibleOdfs_C", m_GlyIsOn_C); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } bool QmitkControlVisualizationPropertiesView::IsPlaneRotated() { mitk::Image* currentImage = dynamic_cast( m_NodeUsedForOdfVisualization->GetData() ); if( currentImage == nullptr ) { MITK_ERROR << " Casting problems. Returning false"; return false; } mitk::Vector3D imageNormal0 = currentImage->GetSlicedGeometry()->GetAxisVector(0); mitk::Vector3D imageNormal1 = currentImage->GetSlicedGeometry()->GetAxisVector(1); mitk::Vector3D imageNormal2 = currentImage->GetSlicedGeometry()->GetAxisVector(2); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); auto renderWindowPart = this->GetRenderWindowPart(); double eps = 0.000001; // for all 2D renderwindows of the render window part check alignment { mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast ( renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer()->GetCurrentWorldPlaneGeometry() ); if (displayPlane.IsNull()) { return false; } mitk::Vector3D normal = displayPlane->GetNormal(); normal.Normalize(); int test = 0; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal0.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal1.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal2.GetVnlVector()))-1) > eps ) { test++; } if (test==3) { return true; } } { mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast ( renderWindowPart->GetQmitkRenderWindow("sagittal")->GetRenderer()->GetCurrentWorldPlaneGeometry() ); if (displayPlane.IsNull()) { return false; } mitk::Vector3D normal = displayPlane->GetNormal(); normal.Normalize(); int test = 0; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal0.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal1.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal2.GetVnlVector()))-1) > eps ) { test++; } if (test==3) { return true; } } { mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast ( renderWindowPart->GetQmitkRenderWindow("coronal")->GetRenderer()->GetCurrentWorldPlaneGeometry() ); if (displayPlane.IsNull()) { return false; } mitk::Vector3D normal = displayPlane->GetNormal(); normal.Normalize(); int test = 0; if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal0.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal1.GetVnlVector()))-1) > eps ) { test++; } if( fabs(fabs(dot_product(normal.GetVnlVector(),imageNormal2.GetVnlVector()))-1) > eps ) { test++; } if (test==3) { return true; } } return false; } void QmitkControlVisualizationPropertiesView::ShowMaxNumberChanged() { int maxNr = m_Controls->m_ShowMaxNumber->value(); if ( maxNr < 1 ) { m_Controls->m_ShowMaxNumber->setValue( 1 ); maxNr = 1; } if ( dynamic_cast(m_SelectedNode->GetData()) || dynamic_cast(m_SelectedNode->GetData()) || dynamic_cast(m_SelectedNode->GetData()) ) { m_SelectedNode->SetIntProperty("ShowMaxNumber", maxNr); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::NormalizationDropdownChanged(int normDropdown) { typedef mitk::OdfNormalizationMethodProperty PropType; PropType::Pointer normMeth = PropType::New(); switch(normDropdown) { case 0: normMeth->SetNormalizationToMinMax(); break; case 1: normMeth->SetNormalizationToMax(); break; case 2: normMeth->SetNormalizationToNone(); break; case 3: normMeth->SetNormalizationToGlobalMax(); break; default: normMeth->SetNormalizationToMinMax(); } if ( dynamic_cast(m_SelectedNode->GetData()) || dynamic_cast(m_SelectedNode->GetData()) || dynamic_cast(m_SelectedNode->GetData()) ) { m_SelectedNode->SetProperty("Normalization", normMeth.GetPointer()); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::ScalingFactorChanged(double scalingFactor) { if ( dynamic_cast(m_SelectedNode->GetData()) || dynamic_cast(m_SelectedNode->GetData()) || dynamic_cast(m_SelectedNode->GetData()) ) { m_SelectedNode->SetFloatProperty("Scaling", scalingFactor); } if (auto renderWindowPart = this->GetRenderWindowPart()) { renderWindowPart->RequestUpdate(); } } void QmitkControlVisualizationPropertiesView::AdditionalScaling(int additionalScaling) { typedef mitk::OdfScaleByProperty PropType; PropType::Pointer scaleBy = PropType::New(); switch(additionalScaling) { case 0: scaleBy->SetScaleByNothing(); break; case 1: scaleBy->SetScaleByGFA(); //m_Controls->params_frame->setVisible(true); break; default: scaleBy->SetScaleByNothing(); } if ( dynamic_cast(m_SelectedNode->GetData()) || dynamic_cast(m_SelectedNode->GetData()) || dynamic_cast(m_SelectedNode->GetData()) ) { m_SelectedNode->SetProperty("ScaleBy", scaleBy.GetPointer()); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkControlVisualizationPropertiesView::Fiber2DfadingEFX() { if (m_SelectedNode && dynamic_cast(m_SelectedNode->GetData()) ) { bool currentMode; m_SelectedNode->GetBoolProperty("Fiber2DfadeEFX", currentMode); m_SelectedNode->SetProperty("Fiber2DfadeEFX", mitk::BoolProperty::New(!currentMode)); dynamic_cast(m_SelectedNode->GetData())->RequestUpdate2D(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::FiberSlicingThickness2D() { if (m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { float fibThickness = m_Controls->m_FiberThicknessSlider->value() * 0.1; float currentThickness = 0; m_SelectedNode->GetFloatProperty("Fiber2DSliceThickness", currentThickness); if ( fabs(fibThickness-currentThickness) < 0.001 ) { return; } m_SelectedNode->SetProperty("Fiber2DSliceThickness", mitk::FloatProperty::New(fibThickness)); dynamic_cast(m_SelectedNode->GetData())->RequestUpdate2D(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::FiberSlicingUpdateLabel(int value) { QString label = "Range %1 mm"; label = label.arg(value * 0.1); m_Controls->label_range->setText(label); FiberSlicingThickness2D(); } void QmitkControlVisualizationPropertiesView::SetCustomColor(const itk::EventObject& /*e*/) { if(m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { float color[3]; m_SelectedNode->GetColor(color); mitk::FiberBundle::Pointer fib = dynamic_cast(m_SelectedNode->GetData()); fib->SetFiberColors(color[0]*255, color[1]*255, color[2]*255); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if (m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { float color[3]; m_SelectedNode->GetColor(color); mitk::PeakImage::Pointer img = dynamic_cast(m_SelectedNode->GetData()); img->SetCustomColor(color[0]*255, color[1]*255, color[2]*255); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::ResetColoring() { if(m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { mitk::FiberBundle::Pointer fib = dynamic_cast(m_SelectedNode->GetData()); fib->ColorFibersByOrientation(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else if(m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { mitk::PeakImage::Pointer fib = dynamic_cast(m_SelectedNode->GetData()); fib->ColorByOrientation(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::PlanarFigureFocus() { if(m_SelectedNode) { mitk::PlanarFigure* _PlanarFigure = 0; _PlanarFigure = dynamic_cast (m_SelectedNode->GetData()); if (_PlanarFigure && _PlanarFigure->GetPlaneGeometry()) { QmitkRenderWindow* selectedRenderWindow = 0; bool PlanarFigureInitializedWindow = false; auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); QmitkRenderWindow* axialRenderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); if (m_SelectedNode->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, axialRenderWindow->GetRenderer())) { selectedRenderWindow = axialRenderWindow; } QmitkRenderWindow* sagittalRenderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, sagittalRenderWindow->GetRenderer())) { selectedRenderWindow = sagittalRenderWindow; } QmitkRenderWindow* coronalRenderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, coronalRenderWindow->GetRenderer())) { selectedRenderWindow = coronalRenderWindow; } QmitkRenderWindow* _3DRenderWindow = renderWindowPart->GetQmitkRenderWindow("3d"); if (!selectedRenderWindow && m_SelectedNode->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, _3DRenderWindow->GetRenderer())) { selectedRenderWindow = _3DRenderWindow; } const mitk::PlaneGeometry* _PlaneGeometry = _PlanarFigure->GetPlaneGeometry(); mitk::VnlVector normal = _PlaneGeometry->GetNormalVnl(); mitk::PlaneGeometry::ConstPointer worldGeometry1 = axialRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); mitk::PlaneGeometry::ConstPointer _Plane1 = dynamic_cast( worldGeometry1.GetPointer() ); mitk::VnlVector normal1 = _Plane1->GetNormalVnl(); mitk::PlaneGeometry::ConstPointer worldGeometry2 = sagittalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); mitk::PlaneGeometry::ConstPointer _Plane2 = dynamic_cast( worldGeometry2.GetPointer() ); mitk::VnlVector normal2 = _Plane2->GetNormalVnl(); mitk::PlaneGeometry::ConstPointer worldGeometry3 = coronalRenderWindow->GetRenderer()->GetCurrentWorldPlaneGeometry(); mitk::PlaneGeometry::ConstPointer _Plane3 = dynamic_cast( worldGeometry3.GetPointer() ); mitk::VnlVector normal3 = _Plane3->GetNormalVnl(); normal[0] = fabs(normal[0]); normal[1] = fabs(normal[1]); normal[2] = fabs(normal[2]); normal1[0] = fabs(normal1[0]); normal1[1] = fabs(normal1[1]); normal1[2] = fabs(normal1[2]); normal2[0] = fabs(normal2[0]); normal2[1] = fabs(normal2[1]); normal2[2] = fabs(normal2[2]); normal3[0] = fabs(normal3[0]); normal3[1] = fabs(normal3[1]); normal3[2] = fabs(normal3[2]); double ang1 = angle(normal, normal1); double ang2 = angle(normal, normal2); double ang3 = angle(normal, normal3); if(ang1 < ang2 && ang1 < ang3) { selectedRenderWindow = axialRenderWindow; } else { if(ang2 < ang3) { selectedRenderWindow = sagittalRenderWindow; } else { selectedRenderWindow = coronalRenderWindow; } } // make node visible if (selectedRenderWindow) { const mitk::Point3D& centerP = _PlaneGeometry->GetOrigin(); selectedRenderWindow->GetSliceNavigationController()->ReorientSlices( centerP, _PlaneGeometry->GetNormal()); } } // set interactor for new node (if not already set) mitk::PlanarFigureInteractor::Pointer figureInteractor = dynamic_cast(m_SelectedNode->GetDataInteractor().GetPointer()); if(figureInteractor.IsNull()) { figureInteractor = mitk::PlanarFigureInteractor::New(); us::Module* planarFigureModule = us::ModuleRegistry::GetModule( "MitkPlanarFigure" ); figureInteractor->LoadStateMachine("PlanarFigureInteraction.xml", planarFigureModule ); figureInteractor->SetEventConfig( "PlanarFigureConfig.xml", planarFigureModule ); figureInteractor->SetDataNode( m_SelectedNode ); } m_SelectedNode->SetProperty("planarfigure.iseditable",mitk::BoolProperty::New(true)); } } void QmitkControlVisualizationPropertiesView::SetInteractor() { // BUG 19179 // typedef std::vector Container; // Container _NodeSet = this->GetDataManagerSelection(); // mitk::DataNode* node = 0; // mitk::FiberBundle* bundle = 0; // mitk::FiberBundleInteractor::Pointer bundleInteractor = 0; // // finally add all nodes to the model // for(Container::const_iterator it=_NodeSet.begin(); it!=_NodeSet.end() // ; it++) // { // node = const_cast(*it); // bundle = dynamic_cast(node->GetData()); // if(bundle) // { // bundleInteractor = dynamic_cast(node->GetInteractor()); // if(bundleInteractor.IsNotNull()) // mitk::GlobalInteraction::GetInstance()->RemoveInteractor(bundleInteractor); // if(!m_Controls->m_Crosshair->isChecked()) // { // m_Controls->m_Crosshair->setChecked(false); // this->GetActiveStdMultiWidget()->GetRenderWindow4()->setCursor(Qt::ArrowCursor); // m_CurrentPickingNode = 0; // } // else // { // m_Controls->m_Crosshair->setChecked(true); // bundleInteractor = mitk::FiberBundleInteractor::New("FiberBundleInteractor", node); // mitk::GlobalInteraction::GetInstance()->AddInteractor(bundleInteractor); // this->GetActiveStdMultiWidget()->GetRenderWindow4()->setCursor(Qt::CrossCursor); // m_CurrentPickingNode = node; // } // } // } } void QmitkControlVisualizationPropertiesView::TubeRadiusChanged() { if(m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { float newRadius = m_Controls->m_TubeWidth->value(); m_SelectedNode->SetFloatProperty("shape.tuberadius", newRadius); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::RibbonWidthChanged() { if(m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { float newWidth = m_Controls->m_RibbonWidth->value(); m_SelectedNode->SetFloatProperty("shape.ribbonwidth", newWidth); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::LineWidthChanged() { if(m_SelectedNode && dynamic_cast(m_SelectedNode->GetData())) { auto newWidth = m_Controls->m_LineWidth->value(); float currentWidth = 0; m_SelectedNode->SetFloatProperty("shape.linewidth", currentWidth); if (currentWidth==newWidth) return; m_SelectedNode->SetFloatProperty("shape.linewidth", newWidth); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkControlVisualizationPropertiesView::Welcome() { berry::PlatformUI::GetWorkbench()->GetIntroManager() ->ShowIntro(GetSite()->GetWorkbenchWindow(), false); } void QmitkControlVisualizationPropertiesView::OnColourisationModeChanged() { if( m_SelectedNode && m_NodeUsedForOdfVisualization.IsNotNull() ) { m_SelectedNode->SetProperty( "DiffusionCore.Rendering.OdfVtkMapper.ColourisationModeBit", mitk::BoolProperty::New( m_Controls->m_OdfColorBox->currentIndex() ) ); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } else { MITK_DEBUG << "QmitkControlVisualizationPropertiesView::OnColourisationModeChanged() was called but m_NodeUsedForOdfVisualization was Null."; } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesViewControls.ui index b6aa4a390d..6713f527a0 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging/src/internal/QmitkControlVisualizationPropertiesViewControls.ui @@ -1,1001 +1,1001 @@ QmitkControlVisualizationPropertiesViewControls 0 0 567 619 0 100 0 0 QmitkTemplate QCommandLinkButton:disabled { border: none; } QGroupBox { background-color: transparent; } 0 0 0 0 QFrame::NoFrame QFrame::Raised 0 0 0 0 0 0 QFrame::NoFrame QFrame::Raised 0 0 0 0 Multislice Projection MIP QToolButton::MenuButtonPopup Qt::NoArrow QFrame::NoFrame QFrame::Plain 0 0 0 0 Toggle visibility of ODF glyphs (axial) :/QmitkDiffusionImaging/glyphsoff_T.png :/QmitkDiffusionImaging/glyphson_T.png:/QmitkDiffusionImaging/glyphsoff_T.png true false Toggle visibility of ODF glyphs (sagittal) :/QmitkDiffusionImaging/glyphsoff_S.png :/QmitkDiffusionImaging/glyphson_S.png:/QmitkDiffusionImaging/glyphsoff_S.png true false Toggle visibility of ODF glyphs (coronal) :/QmitkDiffusionImaging/glyphsoff_C.png :/QmitkDiffusionImaging/glyphson_C.png:/QmitkDiffusionImaging/glyphsoff_C.png true false #Glyphs 9999 Qt::Horizontal 20 20 QFrame::NoFrame QFrame::Raised 0 0 0 0 0 QFrame::NoFrame QFrame::Raised 0 0 0 0 QFrame::NoFrame QFrame::Plain 0 0 0 0 ODF normalization false Min-Max Max None ODF Normalization: ODF normalization false ODF Value Principal Direction Color ODFs/Tensors by: QFrame::NoFrame QFrame::Plain 0 0 0 0 ODF Scale: false None FA/GFA * Additional scaling factor 3 999999999.000000000000000 0.100000000000000 1.000000000000000 QFrame::NoFrame QFrame::Raised 0 0 0 0 QFrame::NoFrame QFrame::Raised 0 0 0 0 - Reset to Default Coloring + Select Fiber Color - :/QmitkDiffusionImaging/color64.gif:/QmitkDiffusionImaging/color64.gif + :/QmitkDiffusionImaging/color64.png:/QmitkDiffusionImaging/color64.png Reset to Default Coloring :/QmitkDiffusionImaging/reset.png:/QmitkDiffusionImaging/reset.png Position Crosshair by 3D-Click :/QmitkDiffusionImaging/crosshair.png:/QmitkDiffusionImaging/crosshair.png true false 2D Fiberfading on/off Qt::Horizontal 40 20 QFrame::NoFrame QFrame::Raised 0 0 0 0 2D Clipping 100 10 10 10 Qt::Horizontal 90 0 10000 16777215 Range QFrame::NoFrame QFrame::Raised 0 0 0 0 Tube Radius 3 0.100000000000000 Ribbon Width 3 0.100000000000000 50 false false <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> <p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#ff0000;">One or more slices are rotated. ODF Visualisation is not possible in rotated planes. Use 'Reinit' on the image node to reset. </span></p></body></html> Qt::AutoText true QFrame::NoFrame QFrame::Raised 0 0 0 0 QFrame::NoFrame QFrame::Raised 0 0 0 0 y z x 0 0 Flip Peaks QFrame::NoFrame QFrame::Raised 0 0 0 0 Reset to Default Coloring :/QmitkDiffusionImaging/reset.png:/QmitkDiffusionImaging/reset.png - Reset to Default Coloring + Select Fiber Color - :/QmitkDiffusionImaging/color64.gif:/QmitkDiffusionImaging/color64.gif + :/QmitkDiffusionImaging/color64.png:/QmitkDiffusionImaging/color64.png Qt::Horizontal 40 20 Line Width 1 1.000000000000000 0.100000000000000 1.000000000000000 3D Clipping 6 6 6 6 0 Coronal Axial No clipping true Sagittal Flipp Clipping Direction Enable 3D Rendering Qt::Vertical 20 40 QmitkDataStorageComboBox.h diff --git a/Plugins/org.mitk.gui.qt.diffusionimagingapp/resources/welcome/pics/3D_4_2.png b/Plugins/org.mitk.gui.qt.diffusionimagingapp/resources/welcome/pics/3D_4_2.png new file mode 100644 index 0000000000..b4ddce91d3 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.diffusionimagingapp/resources/welcome/pics/3D_4_2.png differ diff --git a/Plugins/org.mitk.gui.qt.matchpoint.mapper/documentation/UserManual/Manual.dox b/Plugins/org.mitk.gui.qt.matchpoint.mapper/documentation/UserManual/Manual.dox index f3c8878389..c02863a2a6 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.mapper/documentation/UserManual/Manual.dox +++ b/Plugins/org.mitk.gui.qt.matchpoint.mapper/documentation/UserManual/Manual.dox @@ -1,85 +1,85 @@ /** \page org_mitk_gui_qt_matchpoint_mapper The MatchPoint Image Mapper View \imageMacro{map_mapper_icon_doc.svg, "Icon of the MatchPoint Image Mapper",3} \tableofcontents \section MAP_MAPPER_Introduction Introduction This view offers the possibility to map any image or point set in the data manager using a user selected registration object. Using the Mapper to map images the user can control the field of view (image geometry) the image should be mapped into, as well as interpolation strategy that should be used.\n It is one of several MatchPoint registration plugins.\n Typical usage scenarios\n \li You have registered image I1 onto image I2. Now you want to transfer the segmentation of I1 to I2 in order to evaluate I2 within this mapped segmentation using \ref org_mitk_views_imagestatistics . \li You have registered image I1 onto image I2. Now you want to map I3 (e.g. an other MRI sequence of the same session) also onto I2 with the same registration. \li You have registered image I1 onto image I2. Now you want to map a segmentation done on I1 also onto I2 with the same registration. \li You have registered image I1 onto image I2. Now you want to map a point set of image I1 also onto I2 with the same registration. \section MAP_MAPPER_Contact Contact information This plug-in is being developed by the SIDT group (Software development for Integrated Diagnostics and Therapy) at the DKFZ (German Cancer Research Center). If you have any questions, need support, find a bug or have a feature request, feel free to contact us at www.mitk.org. \section MAP_MAPPER_Usage Usage \imageMacro{map_mapper-examplescreen.png, "Example screenshot showing the Mapper plugin in use.", 14} To use the mapper at least an input data (image or point set) must be selected. Additionally you may select a registration object and a reference image. Registration objects are marked with a small blue icon (e.g. the data "Registration" in the data manager of the screen shot above). The Reference image defines the geometry (field of view, orientation, spacing) that should be used for the result image. By default the view will try to automatically determine the reference image (by default it is the target image of the selected registration). If auto selection cannot determine the reference it will choose the input image as reference. The reference image can be also defined by the user explicitly by activating manual selection.\n REMARK: If you map point sets you can ignore the reference image slot. It has no affect.\n You can multi select registration and data (press the CTRL-key while selecting the nodes in the data manager). The Mapper will automatically sort the selections in the correct "slots" of the view.\n REMARK: The mapping results will be added as child nodes to the used input node.\n REMARK: If you do not select an registration the view will assume that you make an identity transform. This is a convenient way if you just want to resample an image into the geometry of an other image (when no registration is needed). Also in this use case you can take advantage of the different interpolation and sub/super sampling strategies. \imageMacro{map_mapper.png, "Details of the mapper view.", 8} (1) The currently selected registration, that will be used for mapping.\n (2) The currently selected input data, that will be mapped.\n (3) The currently (automatically or by user) selected reference image, that defines the geometry of the result.\n (4) The name of the result data in the data manger.\n (5) The start button(s) to commence the mapping process. For details regarding the two options see \ref MAP_MAPPER_Refine.\n (6) Log windows with messages regarding the mapping process.\n\n Every "slot" has the ability to be locked. If locked the last selection will be kept, regardless the current selection in the data manager. You can use this for example to lock the registration, if you want to map multiple images. Doing so it is enough to just select the next image in the data manager. To lock a slot, click at the "lock" button at the right side (see example images below). \imageMacro{map_node-unlocked.png, "Unlocked slot/node (default state). Changes with the selections in the data manager.",6} \imageMacro{map_node-locked.png, "Locked slot/node. Stays, regardless the selections in the data manager.",6} \section MAP_MAPPER_Refine Mapping or geometry refinement The mapper view offers two options to map images:\n \li "Map" (default) \li "Refine geometry" For images "Map" fills the pixels of the output image by interpolating input image pixels using the registration object. This option always works. But may take longer and introduces interpolation errors, because a new image is resampled.\n The second option "Refine geometry" is only offered, if the registration (more precise its inverse kernel) is matrix based and the selected data is an image. -In this case it just clones the image and refines the image geometry (origin, orientation, ...); thus no interpolation artefacts are introduced. -\remark If you want to use a mapped image in conjunction with the statistic plugin and an mask of the reference image, you must use "Map" to ensure the same geometry. -Otherwise the statistic plugin will fail. +In this case it just clones the image and refines its image geometry (origin and orientation) to project it to the position indicated by the registration; thus no interpolation artefacts are introduced. +\remark If you want to use a mapped image in conjunction with the statistic plugin and an mask of the reference image (or you want to proceed any other computation that expects the voxel to be in the same grid for direct numeric comparison), you must use "Map" to ensure the same geometry +(including the same image grid; including same spacing and resolution). Otherwise operations like the statistic plugin will fail. \section MAP_MAPPER_Settings Settings If you map the image (and not just refine the geometry), you have several settings available:\n \li "Allow undefined pixels": Activate to handle pixels of the result image that are not in the field of view of the input image. This pixel will get the "padding value". \li "Allow error pixels": Activate to handle pixels of the result image that can not be mapped because the registration does not support this part of the output image. This pixel will get the "error value". \li "Interpolator": Set to choose the interpolation strategy that should be used for mapping. \li "Activate super/sub sampling": Activate if you want to use origin and orientation of the reference image but want to alter the spacing. \section MAP_MAPPER_Interpolation Interpolation You can choose from the following interpolation strategies:\n \li "nearest neighbor": Use the value of the nearest pixel. Fastest, but high interpolation errors for gray value images. Right choice for label images or masks. \li "Linear": Fast linear interpolation with often sufficient quality. Tends to blur edges. \li "BSpline (3rd order)": Good trade off between time and quality. \li "Windowed Sinc (Hamming)": Good interpolation quality but very time consuming. \li "Windowed Sinc (Welch)": Good interpolation quality but very time consuming. \section MAP_MAPPER_Masks Handling of masks/segmentations If you select an mask as input image, the plugin will be automatically reconfigured to settings that are suitable for the task of mapping masks. Most importantly the interpolator will be set to "nearest neighbor". */ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake b/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake index 1272eb0b1a..d73169c874 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake @@ -1,102 +1,105 @@ set(SRC_CPP_FILES QmitkMultiLabelSegmentationPreferencePage.cpp ) set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkMultiLabelSegmentationView.cpp QmitkThresholdAction.cpp QmitkAutocropAction.cpp QmitkConvertSurfaceToLabelAction.cpp QmitkConvertMaskToLabelAction.cpp QmitkConvertToMultiLabelSegmentationAction.cpp QmitkCreateMultiLabelSegmentationAction.cpp QmitkLoadMultiLabelPresetAction.cpp QmitkCreateMultiLabelPresetAction.cpp Common/QmitkDataSelectionWidget.cpp SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.cpp SegmentationUtilities/QmitkSegmentationUtilityWidget.cpp SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.cpp SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.cpp SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp + SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.cpp ) set(UI_FILES src/internal/QmitkMultiLabelSegmentationControls.ui src/internal/Common/QmitkDataSelectionWidgetControls.ui src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesViewControls.ui src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui + src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidgetControls.ui ) set(MOC_H_FILES src/QmitkMultiLabelSegmentationPreferencePage.h src/internal/mitkPluginActivator.h src/internal/QmitkMultiLabelSegmentationView.h src/internal/QmitkThresholdAction.h src/internal/QmitkAutocropAction.h src/internal/QmitkConvertSurfaceToLabelAction.h src/internal/QmitkLoadMultiLabelPresetAction.h src/internal/QmitkCreateMultiLabelPresetAction.h src/internal/QmitkConvertMaskToLabelAction.h src/internal/QmitkConvertToMultiLabelSegmentationAction.h src/internal/QmitkCreateMultiLabelSegmentationAction.h src/internal/Common/QmitkDataSelectionWidget.h src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.h src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.h src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.h src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h + src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.h ) set(CACHED_RESOURCE_FILES resources/BooleanDifference_48x48.png resources/BooleanIntersection_48x48.png resources/BooleanOperations_48x48.png resources/BooleanUnion_48x48.png resources/Closing_48x48.png resources/CTKWidgets_48x48.png resources/deformablePlane.png resources/Dilate_48x48.png resources/Erode_48x48.png resources/FillHoles_48x48.png resources/Icons.svg resources/ImageMasking_48x48.png resources/MorphologicalOperations_48x48.png resources/multilabelsegmentation.svg resources/multilabelsegmentation_utilities.svg resources/NewLabel_48x48.png resources/NewSegmentationSession_48x48.png resources/Opening_48x48.png resources/SurfaceToImage_48x48.png plugin.xml ) set(QRC_FILES resources/multilabelsegmentation.qrc resources/MultiLabelSegmentationUtilities.qrc resources/MorphologicalOperationsWidget.qrc resources/BooleanOperationsWidget.qrc ) set(CPP_FILES) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) #usFunctionEmbedResources( #CPP_FILES # LIBRARY_NAME "liborg_mitk_gui_qt_multilabelsegmentation" #ROOT_DIR resources #FILES Interactions/SegmentationInteraction.xml # Interactions/ConfigSegmentation.xml #) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities.qrc b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities.qrc index 4facd08489..44a56f1ae5 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities.qrc +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities.qrc @@ -1,10 +1,11 @@ - - BooleanOperations_48x48.png - ImageMasking_48x48.png - MorphologicalOperations_48x48.png - SurfaceToImage_48x48.png - multilabelsegmentation_utilities.svg - CTKWidgets_48x48.png - + + BooleanOperations_48x48.png + ImageMasking_48x48.png + MorphologicalOperations_48x48.png + SurfaceToImage_48x48.png + multilabelsegmentation_utilities.svg + CTKWidgets_48x48.png + multilabelsegmentation.svg + diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.cpp new file mode 100644 index 0000000000..72bd4b6462 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.cpp @@ -0,0 +1,69 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "QmitkConvertToMlWidget.h" +#include "../../Common/QmitkDataSelectionWidget.h" +#include +#include +#include +#include +#include "src/internal/QmitkConvertToMultiLabelSegmentationAction.h" + +QmitkConvertToMlWidget::QmitkConvertToMlWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent) + : QmitkSegmentationUtilityWidget(timeNavigationController, parent) +{ + m_Controls.setupUi(this); + + m_Controls.dataSelectionWidget->AddDataStorageComboBox(QmitkDataSelectionWidget::ImagePredicate); + + connect(m_Controls.dataSelectionWidget, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(SelectionChanged())); + connect(m_Controls.pushButton, SIGNAL(clicked()), this, SLOT(Convert())); +} + +QmitkConvertToMlWidget::~QmitkConvertToMlWidget() +{ +} + +void QmitkConvertToMlWidget::SelectionChanged() +{ + QmitkDataSelectionWidget* dataSelectionWidget = m_Controls.dataSelectionWidget; + + mitk::DataNode::Pointer node0 = dataSelectionWidget->GetSelection(0); + + if (node0.IsNotNull()) + { + this->EnableButtons(); + } + else + { + this->EnableButtons(false); + } +} + +void QmitkConvertToMlWidget::EnableButtons(bool enable) +{ + m_Controls.pushButton->setEnabled(enable); +} + +void QmitkConvertToMlWidget::Convert() +{ + auto node = m_Controls.dataSelectionWidget->GetSelection(0); + + QmitkConvertToMultiLabelSegmentationAction converter; + QList list; list.append(node); + converter.SetDataStorage(m_Controls.dataSelectionWidget->GetDataStorage()); + converter.Run(list); +} diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.h new file mode 100644 index 0000000000..a9e35e4243 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.h @@ -0,0 +1,41 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef QmitkConvertToMlWidget_h +#define QmitkConvertToMlWidget_h + +#include "../QmitkSegmentationUtilityWidget.h" +#include + +class QmitkConvertToMlWidget : public QmitkSegmentationUtilityWidget +{ + Q_OBJECT + +public: + explicit QmitkConvertToMlWidget(mitk::SliceNavigationController* timeNavigationController, QWidget* parent = nullptr); + ~QmitkConvertToMlWidget(); + +private slots: + void SelectionChanged(); + void Convert(); + +private: + void EnableButtons(bool enable = true); + + Ui::QmitkConvertToMlWidgetControls m_Controls; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidgetControls.ui b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidgetControls.ui new file mode 100644 index 0000000000..4fce705afb --- /dev/null +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidgetControls.ui @@ -0,0 +1,51 @@ + + + QmitkConvertToMlWidgetControls + + + + 0 + 0 + 270 + 300 + + + + + + + + + + Convert + + + + + + + Qt::Vertical + + + + 20 + 273 + + + + + + + + + QmitkDataSelectionWidget + QWidget +
internal/Common/QmitkDataSelectionWidget.h
+ 1 +
+
+ + + + +
diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.cpp b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.cpp index fa613d543d..11ad998f47 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.cpp +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.cpp @@ -1,90 +1,98 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // #define ENABLE_CTK_WIDGETS_WIDGET #include "QmitkMultiLabelSegmentationUtilitiesView.h" #include "BooleanOperations/QmitkBooleanOperationsWidget.h" #include "MorphologicalOperations/QmitkMorphologicalOperationsWidget.h" #include "SurfaceToImage/QmitkSurfaceToImageWidget.h" #include "ImageMasking/QmitkImageMaskingWidget.h" +#include "ConvertToMl/QmitkConvertToMlWidget.h" QmitkMultiLabelSegmentationUtilitiesView::QmitkMultiLabelSegmentationUtilitiesView() : -m_BooleanOperationsWidget(nullptr), -m_MorphologicalOperationsWidget(nullptr), -m_SurfaceToImageWidget(nullptr), -m_ImageMaskingWidget(nullptr) + m_BooleanOperationsWidget(nullptr), + m_MorphologicalOperationsWidget(nullptr), + m_SurfaceToImageWidget(nullptr), + m_ImageMaskingWidget(nullptr), + m_ConvertToMlWidget(nullptr) { } QmitkMultiLabelSegmentationUtilitiesView::~QmitkMultiLabelSegmentationUtilitiesView() { } void QmitkMultiLabelSegmentationUtilitiesView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); mitk::SliceNavigationController* timeNavigationController = renderWindowPart != nullptr ? renderWindowPart->GetTimeNavigationController() : nullptr; m_BooleanOperationsWidget = new QmitkBooleanOperationsWidget(timeNavigationController, parent); m_MorphologicalOperationsWidget = new QmitkMorphologicalOperationsWidget(timeNavigationController, parent); m_SurfaceToImageWidget = new QmitkSurfaceToImageWidget(timeNavigationController, parent); m_ImageMaskingWidget = new QmitkImageMaskingWidget(timeNavigationController, parent); + m_ConvertToMlWidget = new QmitkConvertToMlWidget(timeNavigationController, parent); + this->AddUtilityWidget(m_BooleanOperationsWidget, QIcon(":/MultiLabelSegmentationUtilities/BooleanOperations_48x48.png"), "Boolean Operations"); this->AddUtilityWidget(m_MorphologicalOperationsWidget, QIcon(":/MultiLabelSegmentationUtilities/MorphologicalOperations_48x48.png"), "Morphological Operations"); this->AddUtilityWidget(m_SurfaceToImageWidget, QIcon(":/MultiLabelSegmentationUtilities/SurfaceToImage_48x48.png"), "Surface To Image"); this->AddUtilityWidget(m_ImageMaskingWidget, QIcon(":/MultiLabelSegmentationUtilities/ImageMasking_48x48.png"), "Image Masking"); + + this->AddUtilityWidget(m_ConvertToMlWidget, QIcon(":/MultiLabelSegmentationUtilities/multilabelsegmentation.svg"), "Convert To MultiLabel"); } void QmitkMultiLabelSegmentationUtilitiesView::AddUtilityWidget(QWidget* widget, const QIcon& icon, const QString& text) { m_Controls.toolBox->addItem(widget, icon, text); } void QmitkMultiLabelSegmentationUtilitiesView::SetFocus() { m_Controls.toolBox->setFocus(); } void QmitkMultiLabelSegmentationUtilitiesView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { mitk::SliceNavigationController* timeNavigationController = renderWindowPart->GetTimeNavigationController(); m_BooleanOperationsWidget->SetTimeNavigationController(timeNavigationController); m_MorphologicalOperationsWidget->SetTimeNavigationController(timeNavigationController); m_SurfaceToImageWidget->SetTimeNavigationController(timeNavigationController); m_ImageMaskingWidget->SetTimeNavigationController(timeNavigationController); + m_ConvertToMlWidget->SetTimeNavigationController(timeNavigationController); } void QmitkMultiLabelSegmentationUtilitiesView::RenderWindowPartDeactivated(mitk::IRenderWindowPart*) { m_BooleanOperationsWidget->SetTimeNavigationController(nullptr); m_MorphologicalOperationsWidget->SetTimeNavigationController(nullptr); m_SurfaceToImageWidget->SetTimeNavigationController(nullptr); m_ImageMaskingWidget->SetTimeNavigationController(nullptr); + m_ConvertToMlWidget->SetTimeNavigationController(nullptr); } diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h index 865257ebe3..a770534960 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h @@ -1,57 +1,61 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkMultiLabelSegmentationUtilitiesView_h #define QmitkMultiLabelSegmentationUtilitiesView_h #include #include #include class QmitkBooleanOperationsWidget; class QmitkSurfaceToImageWidget; class QmitkImageMaskingWidget; class QmitkMorphologicalOperationsWidget; +class QmitkMorphologicalOperationsWidget; +class QmitkConvertToMlWidget; class QmitkMultiLabelSegmentationUtilitiesView : public QmitkAbstractView, public mitk::IRenderWindowPartListener { Q_OBJECT public: QmitkMultiLabelSegmentationUtilitiesView(); ~QmitkMultiLabelSegmentationUtilitiesView(); void CreateQtPartControl(QWidget* parent); void SetFocus(); void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart); void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart); private: void AddUtilityWidget(QWidget* widget, const QIcon& icon, const QString& text); QmitkBooleanOperationsWidget* m_BooleanOperationsWidget; QmitkMorphologicalOperationsWidget* m_MorphologicalOperationsWidget; QmitkSurfaceToImageWidget* m_SurfaceToImageWidget; QmitkImageMaskingWidget* m_ImageMaskingWidget; + QmitkConvertToMlWidget* m_ConvertToMlWidget; + Ui::QmitkMultiLabelSegmentationUtilitiesViewControls m_Controls; }; #endif diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp index e197e697c0..649a5d8e92 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.cpp @@ -1,1400 +1,1402 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "MRPerfusionView.h" #include "boost/tokenizer.hpp" #include "boost/math/constants/constants.hpp" #include #include "mitkWorkbenchUtil.h" #include "mitkAterialInputFunctionGenerator.h" #include "mitkConcentrationCurveGenerator.h" #include #include #include #include "mitkThreeStepLinearModelFactory.h" #include "mitkThreeStepLinearModelParameterizer.h" #include #include #include #include #include "mitkTwoCompartmentExchangeModelFactory.h" #include "mitkTwoCompartmentExchangeModelParameterizer.h" #include "mitkNumericTwoCompartmentExchangeModelFactory.h" #include "mitkNumericTwoCompartmentExchangeModelParameterizer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Includes for image casting between ITK and MITK #include #include "mitkImageCast.h" #include "mitkITKImageImport.h" #include #include const std::string MRPerfusionView::VIEW_ID = "org.mitk.gui.qt.pharmacokinetics.mri"; inline double convertToDouble(const std::string& data) { std::istringstream stepStream(data); stepStream.imbue(std::locale("C")); double value = 0.0; if (!(stepStream >> value) || !(stepStream.eof())) { mitkThrow() << "Cannot convert string to double. String: " << data; } return value; } void MRPerfusionView::SetFocus() { m_Controls.btnModelling->setFocus(); } void MRPerfusionView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_Controls.btnModelling->setEnabled(false); m_Controls.errorMessageLabel->hide(); this->InitModelComboBox(); connect(m_Controls.btnModelling, SIGNAL(clicked()), this, SLOT(OnModellingButtonClicked())); connect(m_Controls.comboModel, SIGNAL(currentIndexChanged(int)), this, SLOT(OnModellSet(int))); connect(m_Controls.radioPixelBased, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); //AIF setting m_Controls.groupAIF->hide(); m_Controls.btnAIFFile->setEnabled(false); m_Controls.btnAIFFile->setEnabled(false); m_Controls.radioAIFImage->setChecked(true); m_Controls.comboAIFMask->SetDataStorage(this->GetDataStorage()); m_Controls.comboAIFMask->SetPredicate(m_IsMaskPredicate); m_Controls.comboAIFMask->setVisible(true); m_Controls.comboAIFMask->setEnabled(true); m_Controls.comboAIFImage->SetDataStorage(this->GetDataStorage()); m_Controls.comboAIFImage->SetPredicate(m_IsNoMaskImagePredicate); m_Controls.comboAIFImage->setEnabled(false); m_Controls.checkDedicatedAIFImage->setEnabled(true); m_Controls.HCLSpinBox->setValue(mitk::AterialInputFunctionGenerator::DEFAULT_HEMATOCRIT_LEVEL); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFMask, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.labelAIFMask, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFMask, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.checkDedicatedAIFImage, SLOT(setVisible(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFImage, SLOT(setVisible(bool))); connect(m_Controls.checkDedicatedAIFImage, SIGNAL(toggled(bool)), m_Controls.comboAIFImage, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFImage, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.btnAIFFile, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), m_Controls.aifFilePath, SLOT(setEnabled(bool))); connect(m_Controls.radioAIFFile, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.btnAIFFile, SIGNAL(clicked()), this, SLOT(LoadAIFfromFile())); //Brix setting m_Controls.groupDescBrix->hide(); connect(m_Controls.injectiontime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); //Num2CX setting m_Controls.groupNum2CXM->hide(); connect(m_Controls.odeStepSize, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); //Model fit configuration m_Controls.groupBox_FitConfiguration->hide(); m_Controls.checkBox_Constraints->setEnabled(false); m_Controls.constraintManager->setEnabled(false); m_Controls.initialValuesManager->setEnabled(false); m_Controls.initialValuesManager->setDataStorage(this->GetDataStorage()); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.initialValuesManager, SIGNAL(initialValuesChanged(void)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_StartParameters, SIGNAL(toggled(bool)), m_Controls.initialValuesManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setEnabled(bool))); connect(m_Controls.checkBox_Constraints, SIGNAL(toggled(bool)), m_Controls.constraintManager, SLOT(setVisible(bool))); //Concentration m_Controls.groupConcentration->hide(); m_Controls.groupBoxTurboFlash->hide(); m_Controls.radioButtonNoConversion->setChecked(true); m_Controls.factorSpinBox->setEnabled(false); m_Controls.groupBox_viaT1Map->hide(); connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), m_Controls.groupBoxTurboFlash, SLOT(setVisible(bool))); connect(m_Controls.radioButtonTurboFlash, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.relaxationtime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.recoverytime, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.relaxivity, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButton_absoluteEnhancement, SIGNAL(toggled(bool)), m_Controls.factorSpinBox, SLOT(setEnabled(bool))); connect(m_Controls.radioButton_relativeEnchancement, SIGNAL(toggled(bool)), m_Controls.factorSpinBox, SLOT(setEnabled(bool))); connect(m_Controls.factorSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.radioButtonUsingT1, SIGNAL(toggled(bool)), m_Controls.groupBox_viaT1Map, SLOT(setVisible(bool))); connect(m_Controls.radioButtonUsingT1, SIGNAL(toggled(bool)), this, SLOT(UpdateGUIControls())); connect(m_Controls.FlipangleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.RelaxivitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); connect(m_Controls.TRSpinBox, SIGNAL(valueChanged(double)), this, SLOT(UpdateGUIControls())); m_Controls.ComboT1Map->SetDataStorage(this->GetDataStorage()); m_Controls.ComboT1Map->SetPredicate(m_IsNoMaskImagePredicate); m_Controls.ComboT1Map->setEnabled(false); connect(m_Controls.radioButtonUsingT1, SIGNAL(toggled(bool)), m_Controls.ComboT1Map, SLOT(setEnabled(bool))); UpdateGUIControls(); } bool MRPerfusionView::IsTurboFlashSequenceFlag() const { return this->m_Controls.radioButtonTurboFlash->isChecked(); }; void MRPerfusionView::UpdateGUIControls() { m_Controls.lineFitName->setPlaceholderText(QString::fromStdString(this->GetDefaultFitName())); m_Controls.lineFitName->setEnabled(!m_FittingInProgress); m_Controls.checkBox_Constraints->setEnabled(m_modelConstraints.IsNotNull()); bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool isToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL || dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL || dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool isNum2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; m_Controls.groupAIF->setVisible(isToftsFactory || is2CXMFactory); m_Controls.groupDescBrix->setVisible(isDescBrixFactory); m_Controls.groupNum2CXM->setVisible(isNum2CXMFactory); m_Controls.groupConcentration->setVisible(isToftsFactory || is2CXMFactory); m_Controls.groupBox_FitConfiguration->setVisible(m_selectedModelFactory); m_Controls.groupBox->setEnabled(!m_FittingInProgress); m_Controls.comboModel->setEnabled(!m_FittingInProgress); m_Controls.groupAIF->setEnabled(!m_FittingInProgress); m_Controls.groupDescBrix->setEnabled(!m_FittingInProgress); m_Controls.groupNum2CXM->setEnabled(!m_FittingInProgress); m_Controls.groupConcentration->setEnabled(!m_FittingInProgress); m_Controls.groupBox_FitConfiguration->setEnabled(!m_FittingInProgress); m_Controls.radioROIbased->setEnabled(m_selectedMask.IsNotNull()); m_Controls.btnModelling->setEnabled(m_selectedImage.IsNotNull() && m_selectedModelFactory.IsNotNull() && !m_FittingInProgress && CheckModelSettings()); } void MRPerfusionView::OnModellSet(int index) { m_selectedModelFactory = NULL; if (index > 0) { if (static_cast(index) <= m_FactoryStack.size() ) { m_selectedModelFactory = m_FactoryStack[index - 1]; } else { MITK_WARN << "Invalid model index. Index outside of the factory stack. Factory stack size: "<< m_FactoryStack.size() << "; invalid index: "<< index; } } if (m_selectedModelFactory) { this->m_modelConstraints = dynamic_cast (m_selectedModelFactory->CreateDefaultConstraints().GetPointer()); m_Controls.initialValuesManager->setInitialValues(m_selectedModelFactory->GetParameterNames(), m_selectedModelFactory->GetDefaultInitialParameterization()); if (this->m_modelConstraints.IsNull()) { this->m_modelConstraints = mitk::SimpleBarrierConstraintChecker::New(); } m_Controls.constraintManager->setChecker(this->m_modelConstraints, this->m_selectedModelFactory->GetParameterNames()); } UpdateGUIControls(); } std::string MRPerfusionView::GetFitName() const { std::string fitName = m_Controls.lineFitName->text().toStdString(); if (fitName.empty()) { fitName = m_Controls.lineFitName->placeholderText().toStdString(); } return fitName; } std::string MRPerfusionView::GetDefaultFitName() const { std::string defaultName = "undefined model"; if (this->m_selectedModelFactory.IsNotNull()) { defaultName = this->m_selectedModelFactory->GetClassID(); } if (this->m_Controls.radioPixelBased->isChecked()) { defaultName += "_pixel"; } else { defaultName += "_roi"; } return defaultName; } void MRPerfusionView::OnModellingButtonClicked() { //check if all static parameters set if (m_selectedModelFactory.IsNotNull() && CheckModelSettings()) { + m_HasGeneratedNewInput = false; + m_HasGeneratedNewInputAIF = false; + mitk::ParameterFitImageGeneratorBase::Pointer generator = NULL; mitk::modelFit::ModelFitInfo::Pointer fitSession = NULL; bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool is3LinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool isExtToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool isStanToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool isNum2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; if (isDescBrixFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateDescriptiveBrixModel_PixelBased(fitSession, generator); } else { GenerateDescriptiveBrixModel_ROIBased(fitSession, generator); } } else if (is3LinearFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { Generate3StepLinearModelFit_PixelBased(fitSession, generator); } else { Generate3StepLinearModelFit_ROIBased(fitSession, generator); } } else if (isStanToftsFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (isExtToftsFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (is2CXMFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (isNum2CXMFactory) { if (this->m_Controls.radioPixelBased->isChecked()) { GenerateAIFbasedModelFit_PixelBased(fitSession, generator); } else { GenerateAIFbasedModelFit_ROIBased(fitSession, generator); } } //add other models with else if if (generator.IsNotNull() && fitSession.IsNotNull()) { m_FittingInProgress = true; UpdateGUIControls(); DoFit(fitSession, generator); } else { QMessageBox box; box.setText("Fitting error!"); box.setInformativeText("Could not establish fitting job. Error when setting ab generator, model parameterizer or session info."); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } else { QMessageBox box; box.setText("Static parameters for model are not set!"); box.setInformativeText("Some static parameters, that are needed for calculation are not set and equal to zero. Modeling not possible"); box.setStandardButtons(QMessageBox::Ok); box.setDefaultButton(QMessageBox::Ok); box.setIcon(QMessageBox::Warning); box.exec(); } } void MRPerfusionView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList& selectedNodes) { m_selectedMaskNode = NULL; m_selectedMask = NULL; m_Controls.errorMessageLabel->setText(""); m_Controls.masklabel->setText("No (valid) mask selected."); m_Controls.timeserieslabel->setText("No (valid) series selected."); QList nodes = selectedNodes; if (nodes.size() > 0 && this->m_IsNoMaskImagePredicate->CheckNode(nodes.front())) { this->m_selectedNode = nodes.front(); auto selectedImage = dynamic_cast(this->m_selectedNode->GetData()); m_Controls.timeserieslabel->setText((this->m_selectedNode->GetName()).c_str()); if (selectedImage != this->m_selectedImage) { if (selectedImage) { this->m_Controls.initialValuesManager->setReferenceImageGeometry(selectedImage->GetGeometry()); } else { this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } } this->m_selectedImage = selectedImage; nodes.pop_front(); } else { this->m_selectedNode = NULL; this->m_selectedImage = NULL; this->m_Controls.initialValuesManager->setReferenceImageGeometry(nullptr); } if (nodes.size() > 0 && this->m_IsMaskPredicate->CheckNode(nodes.front())) { this->m_selectedMaskNode = nodes.front(); this->m_selectedMask = dynamic_cast(this->m_selectedMaskNode->GetData()); if (this->m_selectedMask->GetTimeSteps() > 1) { MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit. Mask name: " << m_selectedMaskNode->GetName(); mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedMask = maskedImageTimeSelector->GetOutput(); } m_Controls.masklabel->setText((this->m_selectedMaskNode->GetName()).c_str()); } if (m_selectedMask.IsNull()) { this->m_Controls.radioPixelBased->setChecked(true); } m_Controls.errorMessageLabel->show(); UpdateGUIControls(); } bool MRPerfusionView::CheckModelSettings() const { bool ok = true; //check wether any model is set at all. Otherwise exit with false if (m_selectedModelFactory.IsNotNull()) { bool isDescBrixFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool is3LinearFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool isToftsFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL|| dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool is2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; bool isNum2CXMFactory = dynamic_cast (m_selectedModelFactory.GetPointer()) != NULL; if (isDescBrixFactory) { //if all static parameters for this model are set, exit with true, Otherwise exit with false ok = m_Controls.injectiontime->value() > 0; } else if (is3LinearFactory) { if (this->m_Controls.radioButtonTurboFlash->isChecked()) { ok = ok && (m_Controls.recoverytime->value() > 0); ok = ok && (m_Controls.relaxationtime->value() > 0); ok = ok && (m_Controls.relaxivity->value() > 0); ok = ok && (m_Controls.AifRecoverytime->value() > 0); } else if (this->m_Controls.radioButton_absoluteEnhancement->isChecked() || this->m_Controls.radioButton_relativeEnchancement->isChecked()) { ok = ok && (m_Controls.factorSpinBox->value() > 0); } else if (this->m_Controls.radioButtonUsingT1->isChecked()) { ok = ok && (m_Controls.FlipangleSpinBox->value() > 0); ok = ok && (m_Controls.TRSpinBox->value() > 0); ok = ok && (m_Controls.RelaxivitySpinBox->value() > 0); ok = ok && (m_Controls.ComboT1Map->GetSelectedNode().IsNotNull()); } else if (this->m_Controls.radioButtonNoConversion->isChecked()) { ok = ok; } else { ok = false; } } else if (isToftsFactory || is2CXMFactory || isNum2CXMFactory) { if (this->m_Controls.radioAIFImage->isChecked()) { ok = ok && m_Controls.comboAIFMask->GetSelectedNode().IsNotNull(); if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { ok = ok && m_Controls.comboAIFImage->GetSelectedNode().IsNotNull(); } } else if (this->m_Controls.radioAIFFile->isChecked()) { ok = ok && (this->AIFinputGrid.size() != 0) && (this->AIFinputFunction.size() != 0); } else { ok = false; } if (this->m_Controls.radioButtonTurboFlash->isChecked()) { ok = ok && (m_Controls.recoverytime->value() > 0); ok = ok && (m_Controls.relaxationtime->value() > 0); ok = ok && (m_Controls.relaxivity->value() > 0); ok = ok && (m_Controls.AifRecoverytime->value() > 0); } else if (this->m_Controls.radioButton_absoluteEnhancement->isChecked() || this->m_Controls.radioButton_relativeEnchancement->isChecked()) { ok = ok && (m_Controls.factorSpinBox->value() > 0); } else if (this->m_Controls.radioButtonUsingT1->isChecked()) { ok = ok && (m_Controls.FlipangleSpinBox->value() > 0); ok = ok && (m_Controls.TRSpinBox->value() > 0); ok = ok && (m_Controls.RelaxivitySpinBox->value() > 0); ok = ok && (m_Controls.ComboT1Map->GetSelectedNode().IsNotNull()); } else if (this->m_Controls.radioButtonNoConversion->isChecked()) { ok = ok; } else { ok = false; } if (isNum2CXMFactory) { ok = ok && (this->m_Controls.odeStepSize->value() > 0); } } //add other models as else if and check wether all needed static parameters are set else { ok = false; } if (this->m_Controls.radioButton_StartParameters->isChecked() && !this->m_Controls.initialValuesManager->hasValidInitialValues()) { std::string warning = "Warning. Invalid start parameters. At least one parameter as an invalid image setting as source."; MITK_ERROR << warning; m_Controls.infoBox->append(QString("") + QString::fromStdString(warning) + QString("")); ok = false; }; } else { ok = false; } return ok; } void MRPerfusionView::ConfigureInitialParametersOfParameterizer(mitk::ModelParameterizerBase* parameterizer) const { if (m_Controls.radioButton_StartParameters->isChecked()) { //use user defined initial parameters mitk::InitialParameterizationDelegateBase::Pointer paramDelegate = m_Controls.initialValuesManager->getInitialParametrizationDelegate(); parameterizer->SetInitialParameterizationDelegate(paramDelegate); } } void MRPerfusionView::GenerateDescriptiveBrixModel_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); mitk::DescriptivePharmacokineticBrixModelParameterizer::Pointer modelParameterizer = mitk::DescriptivePharmacokineticBrixModelParameterizer::New(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(m_Controls.injectiontime->value()); mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(this->m_selectedImage); imageTimeSelector->SetTimeNr(0); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::DescriptivePharmacokineticBrixModelParameterizer::BaseImageType::Pointer baseImage; mitk::CastToItkImage(imageTimeSelector->GetOutput(), baseImage); modelParameterizer->SetBaseImage(baseImage); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); } fitGenerator->SetDynamicImage(m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); } void MRPerfusionView::GenerateDescriptiveBrixModel_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::Pointer modelParameterizer = mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::New(); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(m_Controls.injectiontime->value()); modelParameterizer->SetBaseValue(roiSignal[0]); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); std::string roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); } void MRPerfusionView::Generate3StepLinearModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); mitk::ThreeStepLinearModelParameterizer::Pointer modelParameterizer = mitk::ThreeStepLinearModelParameterizer::New(); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); } fitGenerator->SetDynamicImage(m_selectedImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); } void MRPerfusionView::Generate3StepLinearModelFit_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); mitk::ThreeStepLinearModelParameterizer::Pointer modelParameterizer = mitk::ThreeStepLinearModelParameterizer::New(); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); signalGenerator->SetDynamicImage(m_selectedImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Model configuration (static parameters) can be done now this->ConfigureInitialParametersOfParameterizer(modelParameterizer); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(m_selectedImage)); generator = fitGenerator.GetPointer(); std::string roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, m_selectedNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); } template void MRPerfusionView::GenerateAIFbasedModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); - mitk::Image::Pointer concentrationImage; - mitk::DataNode::Pointer concentrationNode; - GetConcentrationImage(concentrationImage, concentrationNode, false); + PrepareConcentrationImage(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; GetAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); mitk::NumericTwoCompartmentExchangeModelParameterizer* numTCXParametrizer = dynamic_cast (modelParameterizer.GetPointer()); if (numTCXParametrizer) { numTCXParametrizer->SetODEINTStepSize(this->m_Controls.odeStepSize->value()); } //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (m_selectedMask.IsNotNull()) { fitGenerator->SetMask(m_selectedMask); roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); } - fitGenerator->SetDynamicImage(concentrationImage); + fitGenerator->SetDynamicImage(this->m_inputImage); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, - concentrationNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), + this->m_inputImage, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } template void MRPerfusionView::GenerateAIFbasedModelFit_ROIBased( mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { if (m_selectedMask.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); - mitk::Image::Pointer concentrationImage; - mitk::DataNode::Pointer concentrationNode; - GetConcentrationImage(concentrationImage, concentrationNode, false); + PrepareConcentrationImage(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; GetAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); this->ConfigureInitialParametersOfParameterizer(modelParameterizer); mitk::NumericTwoCompartmentExchangeModelParameterizer* numTCXParametrizer = dynamic_cast (modelParameterizer.GetPointer()); if (numTCXParametrizer) { numTCXParametrizer->SetODEINTStepSize(this->m_Controls.odeStepSize->value()); } //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(m_selectedMask); - signalGenerator->SetDynamicImage(concentrationImage); + signalGenerator->SetDynamicImage(this->m_inputImage); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Specify fitting strategy and criterion parameters mitk::ModelFitFunctorBase::Pointer fitFunctor = CreateDefaultFitFunctor(modelParameterizer); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(m_selectedMask); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); - fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(concentrationImage)); + fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(this->m_inputImage)); generator = fitGenerator.GetPointer(); std::string roiUID = mitk::EnsureModelFitUID(this->m_selectedMaskNode); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, - concentrationNode->GetData(), mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), + this->m_inputImage, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), this->GetFitName(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); infoSignal.clear(); for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } void MRPerfusionView::DoFit(const mitk::modelFit::ModelFitInfo* fitSession, mitk::ParameterFitImageGeneratorBase* generator) { std::stringstream message; message << "" << "Fitting Data Set . . ." << ""; m_Controls.errorMessageLabel->setText(message.str().c_str()); m_Controls.errorMessageLabel->show(); ///////////////////////// //create job and put it into the thread pool + mitk::modelFit::ModelFitResultNodeVectorType additionalNodes; + if (m_HasGeneratedNewInput) + { + additionalNodes.push_back(m_inputNode); + } + if (m_HasGeneratedNewInputAIF) + { + additionalNodes.push_back(m_inputAIFNode); + } + ParameterFitBackgroundJob* pJob = new ParameterFitBackgroundJob(generator, fitSession, - this->m_selectedNode); + this->m_selectedNode, additionalNodes); pJob->setAutoDelete(true); connect(pJob, SIGNAL(Error(QString)), this, SLOT(OnJobError(QString))); connect(pJob, SIGNAL(Finished()), this, SLOT(OnJobFinished())); connect(pJob, SIGNAL(ResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), this, SLOT(OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType, const ParameterFitBackgroundJob*)), Qt::BlockingQueuedConnection); connect(pJob, SIGNAL(JobProgress(double)), this, SLOT(OnJobProgress(double))); connect(pJob, SIGNAL(JobStatusChanged(QString)), this, SLOT(OnJobStatusChanged(QString))); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); } -MRPerfusionView::MRPerfusionView() : m_FittingInProgress(false) +MRPerfusionView::MRPerfusionView() : m_FittingInProgress(false), m_HasGeneratedNewInput(false), m_HasGeneratedNewInputAIF(false) { m_selectedImage = NULL; m_selectedMask = NULL; mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::ThreeStepLinearModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::StandardToftsModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::ExtendedToftsModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::TwoCompartmentExchangeModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); factory = mitk::NumericTwoCompartmentExchangeModelFactory::New().GetPointer(); m_FactoryStack.push_back(factory); mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isLegacyMask = mitk::NodePredicateAnd::New(isImage, isBinary); mitk::NodePredicateOr::Pointer isMask = mitk::NodePredicateOr::New(isLegacyMask, isLabelSet); mitk::NodePredicateAnd::Pointer isNoMask = mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isMask)); this->m_IsMaskPredicate = mitk::NodePredicateAnd::New(isMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); this->m_IsNoMaskImagePredicate = mitk::NodePredicateAnd::New(isNoMask, mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))).GetPointer(); } void MRPerfusionView::OnJobFinished() { this->m_Controls.infoBox->append(QString("Fitting finished")); this->m_FittingInProgress = false; this->UpdateGUIControls(); }; void MRPerfusionView::OnJobError(QString err) { MITK_ERROR << err.toStdString().c_str(); m_Controls.infoBox->append(QString("") + err + QString("")); }; void MRPerfusionView::OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType results, const ParameterFitBackgroundJob* pJob) { //Store the resulting parameter fit image via convenience helper function in data storage //(handles the correct generation of the nodes and their properties) mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), results, pJob->GetParentNode()); + //this stores the concentration image and AIF concentration image, if generated for this fit in the storage. + //if not generated for this fit, relevant nodes are empty. + mitk::modelFit::StoreResultsInDataStorage(this->GetDataStorage(), pJob->GetAdditionalRelevantNodes(), pJob->GetParentNode()); m_Controls.errorMessageLabel->setText(""); m_Controls.errorMessageLabel->hide(); }; void MRPerfusionView::OnJobProgress(double progress) { QString report = QString("Progress. ") + QString::number(progress); this->m_Controls.infoBox->append(report); }; void MRPerfusionView::OnJobStatusChanged(QString info) { this->m_Controls.infoBox->append(info); } void MRPerfusionView::InitModelComboBox() const { this->m_Controls.comboModel->clear(); this->m_Controls.comboModel->addItem(tr("No model selected")); for (ModelFactoryStackType::const_iterator pos = m_FactoryStack.begin(); pos != m_FactoryStack.end(); ++pos) { this->m_Controls.comboModel->addItem(QString::fromStdString((*pos)->GetClassID())); } this->m_Controls.comboModel->setCurrentIndex(0); }; -mitk::DataNode::Pointer MRPerfusionView::AddConcentrationImage(mitk::Image* image, +mitk::DataNode::Pointer MRPerfusionView::GenerateConcentrationNode(mitk::Image* image, const std::string& nodeName) const { if (!image) { mitkThrow() << "Cannot generate concentration node. Passed image is null. parameter name: "; } mitk::DataNode::Pointer result = mitk::DataNode::New(); result->SetData(image); - result->SetName(nodeName); - result->SetVisibility(true); mitk::EnsureModelFitUID(result); - this->GetDataStorage()->Add(result, m_selectedNode); - return result; }; mitk::Image::Pointer MRPerfusionView::ConvertConcentrationImage(bool AIFMode) { //Compute Concentration image mitk::ConcentrationCurveGenerator::Pointer concentrationGen = mitk::ConcentrationCurveGenerator::New(); if (m_Controls.checkDedicatedAIFImage->isChecked() && AIFMode) { concentrationGen->SetDynamicImage(this->m_selectedAIFImage); } else { concentrationGen->SetDynamicImage(this->m_selectedImage); } concentrationGen->SetisTurboFlashSequence(IsTurboFlashSequenceFlag()); concentrationGen->SetAbsoluteSignalEnhancement(m_Controls.radioButton_absoluteEnhancement->isChecked()); concentrationGen->SetRelativeSignalEnhancement(m_Controls.radioButton_relativeEnchancement->isChecked()); concentrationGen->SetUsingT1Map(m_Controls.radioButtonUsingT1->isChecked()); - - + if (IsTurboFlashSequenceFlag()) { if (AIFMode) { concentrationGen->SetRecoveryTime(m_Controls.AifRecoverytime->value()); } else { concentrationGen->SetRecoveryTime(m_Controls.recoverytime->value()); } concentrationGen->SetRelaxationTime(m_Controls.relaxationtime->value()); concentrationGen->SetRelaxivity(m_Controls.relaxivity->value()); } else if (this->m_Controls.radioButtonUsingT1->isChecked()) { concentrationGen->SetRecoveryTime(m_Controls.TRSpinBox->value()); concentrationGen->SetRelaxivity(m_Controls.RelaxivitySpinBox->value()); concentrationGen->SetT10Image(dynamic_cast(m_Controls.ComboT1Map->GetSelectedNode()->GetData())); //Convert Flipangle from degree to radiant double alpha = m_Controls.FlipangleSpinBox->value()/360*2* boost::math::constants::pi(); concentrationGen->SetFlipAngle(alpha); - } + } else { concentrationGen->SetFactor(m_Controls.factorSpinBox->value()); } mitk::Image::Pointer concentrationImage = concentrationGen->GetConvertedImage(); return concentrationImage; } void MRPerfusionView::GetAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif, mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid) { if (this->m_Controls.radioAIFFile->isChecked()) { aif.clear(); aifTimeGrid.clear(); aif.SetSize(AIFinputFunction.size()); aifTimeGrid.SetSize(AIFinputGrid.size()); aif.fill(0.0); aifTimeGrid.fill(0.0); itk::Array::iterator aifPos = aif.begin(); for (std::vector::const_iterator pos = AIFinputFunction.begin(); pos != AIFinputFunction.end(); ++pos, ++aifPos) { *aifPos = *pos; } itk::Array::iterator gridPos = aifTimeGrid.begin(); for (std::vector::const_iterator pos = AIFinputGrid.begin(); pos != AIFinputGrid.end(); ++pos, ++gridPos) { *gridPos = *pos; } } else if (this->m_Controls.radioAIFImage->isChecked()) { aif.clear(); aifTimeGrid.clear(); mitk::AterialInputFunctionGenerator::Pointer aifGenerator = mitk::AterialInputFunctionGenerator::New(); //Hematocrit level aifGenerator->SetHCL(this->m_Controls.HCLSpinBox->value()); //mask settings this->m_selectedAIFMaskNode = m_Controls.comboAIFMask->GetSelectedNode(); this->m_selectedAIFMask = dynamic_cast(this->m_selectedAIFMaskNode->GetData()); if (this->m_selectedAIFMask->GetTimeSteps() > 1) { MITK_INFO << "Selected AIF mask has multiple timesteps. Only use first timestep to mask model fit. AIF Mask name: " << m_selectedAIFMaskNode->GetName() ; mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(this->m_selectedAIFMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); this->m_selectedAIFMask = maskedImageTimeSelector->GetOutput(); } if (this->m_selectedAIFMask.IsNotNull()) { aifGenerator->SetMask(this->m_selectedAIFMask); } //image settings if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { this->m_selectedAIFImageNode = m_Controls.comboAIFImage->GetSelectedNode(); this->m_selectedAIFImage = dynamic_cast(this->m_selectedAIFImageNode->GetData()); } else { this->m_selectedAIFImageNode = m_selectedNode; this->m_selectedAIFImage = m_selectedImage; } - mitk::Image::Pointer concentrationImage; - mitk::DataNode::Pointer concentrationNode; - this->GetConcentrationImage(concentrationImage, concentrationNode, true); + this->PrepareAIFConcentrationImage(); - aifGenerator->SetDynamicImage(concentrationImage); + aifGenerator->SetDynamicImage(this->m_inputAIFImage); aif = aifGenerator->GetAterialInputFunction(); aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid(); } else { mitkThrow() << "Cannot generate AIF. View is in a invalide state. No AIF mode selected."; } - } void MRPerfusionView::LoadAIFfromFile() { QFileDialog dialog; dialog.setNameFilter(tr("Images (*.csv")); QString fileName = dialog.getOpenFileName(); m_Controls.aifFilePath->setText(fileName); std::string m_aifFilePath = fileName.toStdString(); //Read Input typedef boost::tokenizer< boost::escaped_list_separator > Tokenizer; ///////////////////////////////////////////////////////////////////////////////////////////////// //AIF Data std::ifstream in1(m_aifFilePath.c_str()); if (!in1.is_open()) { m_Controls.errorMessageLabel->setText("Could not open AIF File!"); } std::vector< std::string > vec1; std::string line1; while (getline(in1, line1)) { Tokenizer tok(line1); vec1.assign(tok.begin(), tok.end()); - // if (vec1.size() < 3) continue; - this->AIFinputGrid.push_back(convertToDouble(vec1[0])); this->AIFinputFunction.push_back(convertToDouble(vec1[1])); + } +} +void MRPerfusionView::PrepareConcentrationImage() +{ + mitk::Image::Pointer concentrationImage = this->m_selectedImage; + mitk::DataNode::Pointer concentrationNode = this->m_selectedNode; + m_HasGeneratedNewInput = false; + + if (!this->m_Controls.radioButtonNoConversion->isChecked()) + { + concentrationImage = this->ConvertConcentrationImage(false); + concentrationNode = GenerateConcentrationNode(concentrationImage, "Concentration"); + m_HasGeneratedNewInput = true; } + m_inputImage = concentrationImage; + m_inputNode = concentrationNode; + + mitk::EnsureModelFitUID(concentrationNode); } -void MRPerfusionView::GetConcentrationImage(mitk::Image::Pointer& concentrationImage, - mitk::DataNode::Pointer& concentrationNode, bool AIFMode) +void MRPerfusionView::PrepareAIFConcentrationImage() { - if (this->m_Controls.radioButtonNoConversion->isChecked()) + mitk::Image::Pointer concentrationImage = this->m_selectedImage; + mitk::DataNode::Pointer concentrationNode = this->m_selectedNode; + m_HasGeneratedNewInputAIF = false; + + if (this->m_Controls.checkDedicatedAIFImage->isChecked()) { - if (this->m_Controls.checkDedicatedAIFImage->isChecked() && AIFMode) - { - concentrationImage = this->m_selectedAIFImage; - concentrationNode = this->m_selectedAIFImageNode; - } - else - { - concentrationImage = this->m_selectedImage; - concentrationNode = this->m_selectedNode; - } + concentrationImage = this->m_selectedAIFImage; + concentrationNode = this->m_selectedAIFImageNode; } - else + + if (!this->m_Controls.radioButtonNoConversion->isChecked()) { - if (AIFMode && !IsTurboFlashSequenceFlag() && !this->m_Controls.checkDedicatedAIFImage->isChecked()) + if (!IsTurboFlashSequenceFlag() && !this->m_Controls.checkDedicatedAIFImage->isChecked()) { - //we can directly use the input image/node for the AIF if (m_inputImage.IsNull()) { mitkThrow() << - "Cannot get AIF concentration image. Invalid view state. Input image is not defined yet, but should be."; + "Cannot get AIF concentration image. Invalid view state. Input image is not defined yet, but should be."; } + //we can directly use the concentration input image/node (generated by GetConcentrationImage) also for the AIF concentrationImage = this->m_inputImage; concentrationNode = this->m_inputNode; } else { - concentrationImage = this->ConvertConcentrationImage(AIFMode); - - if (AIFMode) - { - concentrationNode = AddConcentrationImage(concentrationImage, "AIF Concentration"); - } - else - { - concentrationNode = AddConcentrationImage(concentrationImage, "Concentration"); - } + concentrationImage = this->ConvertConcentrationImage(true); + concentrationNode = GenerateConcentrationNode(concentrationImage, "AIF Concentration"); + m_HasGeneratedNewInputAIF = true; } } - if (AIFMode) - { - m_inputAIFImage = concentrationImage; - m_inputAIFNode = concentrationNode; - } - else - { - m_inputImage = concentrationImage; - m_inputNode = concentrationNode; - } + m_inputAIFImage = concentrationImage; + m_inputAIFNode = concentrationNode; mitk::EnsureModelFitUID(concentrationNode); } + + mitk::ModelFitFunctorBase::Pointer MRPerfusionView::CreateDefaultFitFunctor( const mitk::ModelParameterizerBase* parameterizer) const { mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor = mitk::LevenbergMarquardtModelFitFunctor::New(); mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 = mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("Chi^2", chi2); if (m_Controls.checkBox_Constraints->isChecked()) { fitFunctor->SetConstraintChecker(m_modelConstraints); } mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel(); ::itk::LevenbergMarquardtOptimizer::ScalesType scales; scales.SetSize(refModel->GetNumberOfParameters()); scales.Fill(1.0); fitFunctor->SetScales(scales); fitFunctor->SetDebugParameterMaps(m_Controls.checkDebug->isChecked()); return fitFunctor.GetPointer(); } diff --git a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.h b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.h index ad27f0cb52..7a67e7400b 100644 --- a/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.h +++ b/Plugins/org.mitk.gui.qt.pharmacokinetics.mri/src/internal/MRPerfusionView.h @@ -1,205 +1,212 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef MRPerfusionView_h #define MRPerfusionView_h #include #include "QmitkAbstractView.h" #include "itkCommand.h" #include "ui_MRPerfusionViewControls.h" #include "mitkModelBase.h" #include "QmitkParameterFitBackgroundJob.h" #include "mitkModelFitResultHelper.h" #include "mitkModelFactoryBase.h" #include "mitkLevenbergMarquardtModelFitFunctor.h" #include "mitkSimpleBarrierConstraintChecker.h" #include "mitkAIFBasedModelBase.h" /*! * @brief Test Plugin for SUV calculations of PET images */ class MRPerfusionView : public QmitkAbstractView { Q_OBJECT public: /*! @brief The view's unique ID - required by MITK */ static const std::string VIEW_ID; MRPerfusionView(); protected slots: void OnModellingButtonClicked(); void OnJobFinished(); void OnJobError(QString err); void OnJobResultsAreAvailable(mitk::modelFit::ModelFitResultNodeVectorType results, const ParameterFitBackgroundJob* pJob); void OnJobProgress(double progress); void OnJobStatusChanged(QString info); void OnModellSet(int); void LoadAIFfromFile(); /**Sets visibility and enabled state of the GUI depending on the settings and workflow state.*/ void UpdateGUIControls(); protected: typedef QList SelectedDataNodeVectorType; // Overridden base class functions /*! * @brief Sets up the UI controls and connects the slots and signals. Gets * called by the framework to create the GUI at the right time. * @param[in,out] parent The parent QWidget, as this class itself is not a QWidget * subclass. */ void CreateQtPartControl(QWidget* parent); /*! * @brief Sets the focus to the plot curve button. Gets called by the framework to set the * focus on the right widget. */ void SetFocus(); /*! @brief Generates a configured fit generator and the corresponding modelinfo for a descriptive brix model with pixel based strategy. * @remark add GenerateFunction for each model in the Combo box*/ void GenerateDescriptiveBrixModel_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator); void GenerateDescriptiveBrixModel_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator); void Generate3StepLinearModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator); void Generate3StepLinearModelFit_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator); template void GenerateAIFbasedModelFit_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator); template void GenerateAIFbasedModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator); /** Helper function that configures the initial parameter strategy of a parameterizer according to the settings of the GUI.*/ void ConfigureInitialParametersOfParameterizer(mitk::ModelParameterizerBase* parameterizer) const; /*! Starts the fitting job with the passed generator and session info*/ void DoFit(const mitk::modelFit::ModelFitInfo* fitSession, mitk::ParameterFitImageGeneratorBase* generator); /**Checks if the settings in the GUI are valid for the chosen model.*/ bool CheckModelSettings() const; void InitModelComboBox() const; - /*! Helper method that adds an concentration image as child node to the current m_selectedNode and returns this new child node.*/ - mitk::DataNode::Pointer AddConcentrationImage(mitk::Image* image, const std::string& nodeName) const; + /*! Helper method that generates a node for the passed concentration image.*/ + mitk::DataNode::Pointer GenerateConcentrationNode(mitk::Image* image, const std::string& nodeName) const; - - - /*! \brief called by QmitkFunctionality when DataManager's selection has changed + /*! \brief called by QmitkFunctionality when DataManager's selection has changed */ virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer source, const QList& selectedNodes); // Variables /*! @brief The view's UI controls */ Ui::MRPerfusionViewControls m_Controls; /* Nodes selected by user/ui for the fit */ mitk::DataNode::Pointer m_selectedNode; mitk::DataNode::Pointer m_selectedMaskNode; mitk::DataNode::Pointer m_selectedAIFMaskNode; mitk::DataNode::Pointer m_selectedAIFImageNode; /* Images selected by user/ui for the fit */ mitk::Image::Pointer m_selectedImage; mitk::Image::Pointer m_selectedMask; mitk::Image::Pointer m_selectedAIFMask; mitk::Image::Pointer m_selectedAIFImage; - /* Node used for the fit (my be the selected image - or converted ones (depending on the ui settings */ - mitk::DataNode::Pointer m_inputNode; - mitk::DataNode::Pointer m_inputAIFNode; - - /* Image used for the fit (my be the selected image - or converted ones (depending on the ui settings */ - mitk::Image::Pointer m_inputImage; - mitk::Image::Pointer m_inputAIFImage; - mitk::ModelFactoryBase::Pointer m_selectedModelFactory; mitk::SimpleBarrierConstraintChecker::Pointer m_modelConstraints; private: bool IsTurboFlashSequenceFlag() const; bool m_FittingInProgress; typedef std::vector ModelFactoryStackType; ModelFactoryStackType m_FactoryStack; /**Converts the selected image to a concentration image based on the given gui settings. AIFMode controls if the concentration image for the fit input or the AIF will be converted.*/ mitk::Image::Pointer ConvertConcentrationImage(bool AIFMode); - /**Helper function that (depending on the gui settings) directly pass back the selected image/node - or the newly generated concentration image/node. The result is always what should be used as input for - the fitting. If AIFMode is true the function will generate the data for the AIF.*/ - void GetConcentrationImage(mitk::Image::Pointer& concentrationImage, - mitk::DataNode::Pointer& concentrationNode, bool AIFMode); + /**Helper function that (depending on the gui settings) prepares m_inputNode and m_inputImage. + Either by directly pass back the selected image/node or the newly generated concentration image/node. + After calling this method m_inputImage are always what should be used as input image + for the fitting.*/ + void PrepareConcentrationImage(); + + /**Helper function that (depending on the gui settings) prepares m_inputAIFNode and m_inputAIFImage. + Either by directly pass back the selected image/node or the newly generated concentration image/node. + After calling this method m_inputAIFImage are always what should be used as AIF image + for the fitting.*/ + void PrepareAIFConcentrationImage(); /**Helper function that (depending on the gui settings) generates and passes back the AIF and its time grid that should be used for fitting. @remark the parameters aif and aifTimeGrid will be initialized accordingly if the method returns.*/ void GetAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif, mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid); /**Helper function that generates a default fitting functor * default is a levenberg marquart based optimizer with all scales set to 1.0. * Constraint setter will be set based on the gui setting and a evaluation parameter * "sum of squared differences" will always be set.*/ mitk::ModelFitFunctorBase::Pointer CreateDefaultFitFunctor(const mitk::ModelParameterizerBase* parameterizer) const; /**Returns the default fit name, derived from the current GUI settings.*/ std::string GetDefaultFitName() const; /**Returns the current set name of the fit (either default name or use defined name).*/ std::string GetFitName() const; std::vector AIFinputGrid; std::vector AIFinputFunction; mitk::NodePredicateBase::Pointer m_IsNoMaskImagePredicate; mitk::NodePredicateBase::Pointer m_IsMaskPredicate; + + /* Node used for the fit (my be the selected image + or converted ones (depending on the ui settings */ + mitk::DataNode::Pointer m_inputNode; + mitk::DataNode::Pointer m_inputAIFNode; + bool m_HasGeneratedNewInput; + bool m_HasGeneratedNewInputAIF; + + /* Image used for the fit (my be the selected image + or converted ones (depending on the ui settings */ + mitk::Image::Pointer m_inputImage; + mitk::Image::Pointer m_inputAIFImage; + }; #endif