diff --git a/Applications/Diffusion/CMakeLists.txt b/Applications/Diffusion/CMakeLists.txt index c1f4e5175e..0e05791fb0 100644 --- a/Applications/Diffusion/CMakeLists.txt +++ b/Applications/Diffusion/CMakeLists.txt @@ -1,97 +1,102 @@ +if(MITK_USE_Python) + project(MitkDiffusion) set(DIFFUSIONAPP_NAME MitkDiffusion) set(_app_options) if(MITK_SHOW_CONSOLE_WINDOW) list(APPEND _app_options SHOW_CONSOLE) endif() # Create a cache entry for the provisioning file which is used to export # the file name in the MITKConfig.cmake file. This will keep external projects # which rely on this file happy. set(DIFFUSIONIMAGINGAPP_PROVISIONING_FILE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${DIFFUSIONAPP_NAME}.provisioning" CACHE INTERNAL "${DIFFUSIONAPP_NAME} provisioning file" FORCE) # should be identical to the list in /CMake/mitkBuildConfigurationmitkDiffusion.cmake # remember to set plugins which should be automatically toggled in target_libraries.cmake set(_plugins org.commontk.configadmin org.commontk.eventadmin org.blueberry.core.runtime org.blueberry.core.expressions org.blueberry.core.commands org.blueberry.ui.qt org.blueberry.ui.qt.log org.blueberry.ui.qt.help org.mitk.core.services org.mitk.gui.common org.mitk.planarfigure org.mitk.core.ext org.mitk.gui.qt.application org.mitk.gui.qt.ext org.mitk.gui.qt.diffusionimagingapp org.mitk.gui.qt.common org.mitk.gui.qt.stdmultiwidgeteditor org.mitk.gui.qt.datamanager org.mitk.gui.qt.measurementtoolbox org.mitk.gui.qt.segmentation org.mitk.gui.qt.volumevisualization org.mitk.gui.qt.diffusionimaging org.mitk.gui.qt.diffusionimaging.connectomics org.mitk.gui.qt.diffusionimaging.fiberfox org.mitk.gui.qt.diffusionimaging.fiberprocessing org.mitk.gui.qt.diffusionimaging.ivim org.mitk.gui.qt.diffusionimaging.odfpeaks org.mitk.gui.qt.diffusionimaging.preprocessing org.mitk.gui.qt.diffusionimaging.reconstruction org.mitk.gui.qt.diffusionimaging.tractography org.mitk.gui.qt.diffusionimaging.registration + org.mitk.gui.qt.diffusionimaging.python org.mitk.gui.qt.diffusionimaging.denoising # org.mitk.gui.qt.matchpoint.algorithm.browser # org.mitk.gui.qt.matchpoint.algorithm.control # org.mitk.gui.qt.matchpoint.mapper org.mitk.gui.qt.imagenavigator org.mitk.gui.qt.moviemaker org.mitk.gui.qt.basicimageprocessing org.mitk.gui.qt.properties org.mitk.gui.qt.viewnavigator ) # Plug-ins listed below will not be # - added as a build-time dependency to the executable # - listed in the provisioning file for the executable # - installed if they are external plug-ins set(_exclude_plugins org.blueberry.test org.blueberry.uitest org.mitk.gui.qt.coreapplication org.mitk.gui.qt.extapplication ) set(_src_files MitkDiffusion.cpp ) qt5_add_resources(_src_files splashscreen.qrc) mitkFunctionCreateBlueBerryApplication( NAME ${DIFFUSIONAPP_NAME} DESCRIPTION "MITK Diffusion" PLUGINS ${_plugins} EXCLUDE_PLUGINS ${_exclude_plugins} SOURCES ${_src_files} ${_app_options} ) mitk_use_modules(TARGET ${DIFFUSIONAPP_NAME} MODULES MitkAppUtil) # Add meta dependencies (e.g. on auto-load modules from depending modules) if(TARGET ${CMAKE_PROJECT_NAME}-autoload) add_dependencies(${DIFFUSIONAPP_NAME} ${CMAKE_PROJECT_NAME}-autoload) endif() # Add a build time dependency to legacy BlueBerry bundles. if(MITK_MODULES_ENABLED_PLUGINS) add_dependencies(${DIFFUSIONAPP_NAME} ${MITK_MODULES_ENABLED_PLUGINS}) endif() + +endif() # MITK_USE_PYTHON diff --git a/Applications/Diffusion/target_libraries.cmake b/Applications/Diffusion/target_libraries.cmake index d9241ad619..022c63d39c 100644 --- a/Applications/Diffusion/target_libraries.cmake +++ b/Applications/Diffusion/target_libraries.cmake @@ -1,33 +1,34 @@ # A list of plug-in targets which should be automatically enabled # (or be available in external projects) for this application set(target_libraries org_blueberry_ui_qt org_blueberry_ui_qt_help org_mitk_planarfigure org_mitk_gui_qt_diffusionimagingapp org_mitk_gui_qt_ext org_mitk_gui_qt_datamanager org_mitk_gui_qt_segmentation org_mitk_gui_qt_volumevisualization org_mitk_gui_qt_diffusionimaging org_mitk_gui_qt_diffusionimaging_connectomics org_mitk_gui_qt_diffusionimaging_fiberfox org_mitk_gui_qt_diffusionimaging_fiberprocessing org_mitk_gui_qt_diffusionimaging_ivim org_mitk_gui_qt_diffusionimaging_odfpeaks org_mitk_gui_qt_diffusionimaging_preprocessing org_mitk_gui_qt_diffusionimaging_reconstruction org_mitk_gui_qt_diffusionimaging_tractography org_mitk_gui_qt_diffusionimaging_registration + org_mitk_gui_qt_diffusionimaging_python org_mitk_gui_qt_diffusionimaging_denoising # org_mitk_gui_qt_matchpoint_algorithm_browser # org_mitk_gui_qt_matchpoint_algorithm_control # org_mitk_gui_qt_matchpoint_mapper org_mitk_gui_qt_imagenavigator org_mitk_gui_qt_moviemaker org_mitk_gui_qt_measurementtoolbox org_mitk_gui_qt_basicimageprocessing org_mitk_gui_qt_viewnavigator ) diff --git a/CMake/BuildConfigurations/DiffusionAll.cmake b/CMake/BuildConfigurations/DiffusionAll.cmake index c1ca6adcf2..449fe3c02f 100644 --- a/CMake/BuildConfigurations/DiffusionAll.cmake +++ b/CMake/BuildConfigurations/DiffusionAll.cmake @@ -1,37 +1,38 @@ message(STATUS "Configuring MITK Diffusion with all Plugins") # 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_Python ON CACHE BOOL "" FORCE) +set(MITK_USE_BetData ON CACHE BOOL "" FORCE) set(MITK_USE_SYSTEM_PYTHON 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_Diffusion ON CACHE BOOL "Build MITK Diffusion" FORCE) # Activate Diffusion Mini Apps set(BUILD_DiffusionCoreCmdApps ON CACHE BOOL "Build commandline tools for diffusion" FORCE) 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 ON CACHE BOOL "Build commandline tools for diffusion connectomics" 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 ON 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) # Disable 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) diff --git a/CMake/BuildConfigurations/DiffusionRelease.cmake b/CMake/BuildConfigurations/DiffusionRelease.cmake index 144f7174dd..4945db9bc6 100644 --- a/CMake/BuildConfigurations/DiffusionRelease.cmake +++ b/CMake/BuildConfigurations/DiffusionRelease.cmake @@ -1,38 +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_Python ON CACHE BOOL "" FORCE) +set(MITK_USE_BetData ON CACHE BOOL "" FORCE) +set(MITK_USE_SYSTEM_PYTHON 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_DiffusionCoreCmdApps OFF CACHE BOOL "Build commandline tools for diffusion" FORCE) 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 OFF CACHE BOOL "Build miscellaneous commandline tools for diffusion" FORCE) set(BUILD_DiffusionQuantificationCmdApps OFF 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 OFF CACHE BOOL "Build commandline tools for diffusion fiber tractography evaluation" FORCE) set(BUILD_DiffusionConnectomicsCmdApps ON CACHE BOOL "Build commandline tools for diffusion connectomics" 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(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE) diff --git a/CMake/FindBetData.cmake b/CMake/FindBetData.cmake new file mode 100644 index 0000000000..f15a41f9f2 --- /dev/null +++ b/CMake/FindBetData.cmake @@ -0,0 +1,7 @@ +set(BetData_DIR ${MITK_SUPERBUILD_BINARY_DIR}/ep/src/BetData) + +if(EXISTS ${BetData_DIR}) + set(BetData_FOUND TRUE) +else() + set(BetData_FOUND FALSE) +endif() diff --git a/CMake/PackageDepends/MITK_BetData_Config.cmake b/CMake/PackageDepends/MITK_BetData_Config.cmake new file mode 100644 index 0000000000..e69de29bb2 diff --git a/CMake/mitkInstallRules.cmake b/CMake/mitkInstallRules.cmake index 01d2b71efc..8675299116 100644 --- a/CMake/mitkInstallRules.cmake +++ b/CMake/mitkInstallRules.cmake @@ -1,193 +1,199 @@ MITK_INSTALL(FILES "${MITK_SOURCE_DIR}/mitk.ico") MITK_INSTALL(FILES "${MITK_SOURCE_DIR}/mitk.bmp") # Install CTK Qt (designer) plugins if(MITK_USE_CTK) if(EXISTS ${CTK_QTDESIGNERPLUGINS_DIR}) set(_qtplugin_install_destinations) if(MACOSX_BUNDLE_NAMES) foreach(bundle_name ${MACOSX_BUNDLE_NAMES}) list(APPEND _qtplugin_install_destinations ${bundle_name}.app/Contents/MacOS/${_install_DESTINATION}/plugins/designer) endforeach() else() list(APPEND _qtplugin_install_destinations bin/plugins/designer) endif() set(_ctk_qt_plugin_folder_release) set(_ctk_qt_plugin_folder_debug) if(NOT CMAKE_CFG_INTDIR STREQUAL ".") set(_ctk_qt_plugin_folder_release "Release/") set(_ctk_qt_plugin_folder_debug "Debug/") endif() foreach(_qtplugin_install_dir ${_qtplugin_install_destinations}) install(DIRECTORY "${CTK_QTDESIGNERPLUGINS_DIR}/designer/${_ctk_qt_plugin_folder_release}" DESTINATION ${_qtplugin_install_dir} CONFIGURATIONS Release ) install(DIRECTORY "${CTK_QTDESIGNERPLUGINS_DIR}/designer/${_ctk_qt_plugin_folder_debug}" DESTINATION ${_qtplugin_install_dir} CONFIGURATIONS Debug ) endforeach() endif() endif() # related to MITK:T19679 if(MACOSX_BUNDLE_NAMES) foreach(bundle_name ${MACOSX_BUNDLE_NAMES}) get_property(_qmake_location TARGET ${Qt5Core_QMAKE_EXECUTABLE} PROPERTY IMPORT_LOCATION) get_filename_component(_qmake_path "${_qmake_location}" DIRECTORY) install(FILES "${_qmake_path}/../plugins/platforms/libqcocoa.dylib" DESTINATION "${bundle_name}.app/Contents/MacOS/platforms" CONFIGURATIONS Release) install(FILES "${_qmake_path}/../plugins/sqldrivers/libqsqlite.dylib" DESTINATION "${bundle_name}.app/Contents/MacOS/sqldrivers" CONFIGURATIONS Release) install(FILES "${_qmake_path}/../plugins/iconengines/libqsvgicon.dylib" DESTINATION "${bundle_name}.app/Contents/MacOS/iconengines" CONFIGURATIONS Release) # related to MITK:T19679-InstallQtWebEnginProcess if(MITK_USE_Qt5_WebEngine) get_filename_component(ABS_DIR_HELPERS "${_qmake_path}/../lib/QtWebEngineCore.framework/Helpers" REALPATH) install(DIRECTORY ${ABS_DIR_HELPERS} DESTINATION "${bundle_name}.app/Contents/Frameworks/QtWebEngineCore.framework/" CONFIGURATIONS Release) endif() endforeach() endif() if(WIN32) if(MITK_USE_Qt5) get_property(_qmake_location TARGET ${Qt5Core_QMAKE_EXECUTABLE} PROPERTY IMPORT_LOCATION) get_filename_component(_qmake_path "${_qmake_location}" DIRECTORY) install(FILES "${_qmake_path}/../plugins/platforms/qwindows.dll" DESTINATION "bin/plugins/platforms" CONFIGURATIONS Release) install(FILES "${_qmake_path}/../plugins/sqldrivers/qsqlite.dll" DESTINATION "bin/plugins/sqldrivers" CONFIGURATIONS Release) install(FILES "${_qmake_path}/../plugins/imageformats/qsvg.dll" DESTINATION "bin/plugins/imageformats" CONFIGURATIONS Release) install(FILES "${_qmake_path}/../plugins/iconengines/qsvgicon.dll" DESTINATION "bin/plugins/iconengines" CONFIGURATIONS Release) if(MITK_USE_Qt5_WebEngine) MITK_INSTALL( FILES "${_qmake_path}/QtWebEngineProcess.exe") endif() install(DIRECTORY "${_qmake_path}/../resources/" DESTINATION "bin/resources/" CONFIGURATIONS Release) install(DIRECTORY "${_qmake_path}/../translations/qtwebengine_locales/" DESTINATION "bin/translations/qtwebengine_locales/" CONFIGURATIONS Release) install(FILES "${_qmake_path}/../plugins/platforms/qwindowsd.dll" DESTINATION "bin/plugins/platforms" CONFIGURATIONS Debug) install(FILES "${_qmake_path}/../plugins/sqldrivers/qsqlited.dll" DESTINATION "bin/plugins/sqldrivers" CONFIGURATIONS Debug) install(FILES "${_qmake_path}/../plugins/imageformats/qsvgd.dll" DESTINATION "bin/plugins/imageformats" CONFIGURATIONS Debug) install(FILES "${_qmake_path}/../plugins/iconengines/qsvgicond.dll" DESTINATION "bin/plugins/iconengines" CONFIGURATIONS Debug) install(DIRECTORY "${_qmake_path}/../resources/" DESTINATION "bin/resources/" CONFIGURATIONS Debug) install(DIRECTORY "${_qmake_path}/../translations/qtwebengine_locales/" DESTINATION "bin/translations/qtwebengine_locales/" CONFIGURATIONS Debug) endif() #DCMTK Dlls install target (shared libs on gcc only) if(MINGW AND DCMTK_ofstd_LIBRARY) set(_dcmtk_libs ${DCMTK_dcmdata_LIBRARY} ${DCMTK_dcmimgle_LIBRARY} ${DCMTK_dcmnet_LIBRARY} ${DCMTK_ofstd_LIBRARY} ) foreach(_dcmtk_lib ${_dcmtk_libs}) MITK_INSTALL(FILES ${_dcmtk_lib} ) endforeach() endif() #MinGW dll if(MINGW) find_library(MINGW_RUNTIME_DLL "mingwm10.dll" HINTS ${CMAKE_FIND_ROOT_PATH}/sys-root/mingw/bin) if(MINGW_RUNTIME_DLL) MITK_INSTALL(FILES ${MINGW_RUNTIME_DLL} ) else() message(SEND_ERROR "Could not find mingwm10.dll which is needed for a proper install") endif() find_library(MINGW_GCC_RUNTIME_DLL "libgcc_s_dw2-1.dll" HINTS ${CMAKE_FIND_ROOT_PATH}/sys-root/mingw/bin) if(MINGW_GCC_RUNTIME_DLL) MITK_INSTALL(FILES ${MINGW_GCC_RUNTIME_DLL} ) else() message(SEND_ERROR "Could not find libgcc_s_dw2-1.dll which is needed for a proper install") endif() endif() else() #DCMTK Dlls install target (shared libs on gcc only) if(DCMTK_ofstd_LIBRARY) set(_dcmtk_libs ${DCMTK_dcmdata_LIBRARY} ${DCMTK_dcmimgle_LIBRARY} ${DCMTK_dcmnet_LIBRARY} ${DCMTK_ofstd_LIBRARY} ) foreach(_dcmtk_lib ${_dcmtk_libs}) #MITK_INSTALL(FILES ${_dcmtk_lib} DESTINATION lib) endforeach() endif() # We need to install Webengineprocess and related files on unix as well if(UNIX) if(MITK_USE_Qt5) get_property(_qmake_location TARGET ${Qt5Core_QMAKE_EXECUTABLE} PROPERTY IMPORT_LOCATION) get_filename_component(_qmake_path "${_qmake_location}" DIRECTORY) install(FILES "${_qmake_path}/../plugins/platforms/libqxcb.so" DESTINATION "bin/plugins/platforms") install(FILES "${_qmake_path}/../plugins/sqldrivers/libqsqlite.so" DESTINATION "bin/plugins/sqldrivers") install(FILES "${_qmake_path}/../plugins/imageformats/libqsvg.so" DESTINATION "bin/plugins/imageformats") install(FILES "${_qmake_path}/../plugins/iconengines/libqsvgicon.so" DESTINATION "bin/plugins/iconengines") install(FILES "${_qmake_path}/../plugins/xcbglintegrations/libqxcb-glx-integration.so" DESTINATION "bin/plugins/xcbglintegrations") if(MITK_USE_Qt5_WebEngine) MITK_INSTALL_HELPER_APP( EXECUTABLES "${_qmake_path}/../libexec/QtWebEngineProcess") endif() install(DIRECTORY "${_qmake_path}/../resources/" DESTINATION "bin/resources/") install(DIRECTORY "${_qmake_path}/../translations/qtwebengine_locales/" DESTINATION "bin/translations/qtwebengine_locales/") endif() endif() endif() #install Matchpoint libs that are currently not auto detected if(MITK_USE_MatchPoint) install(DIRECTORY "${MITK_EXTERNAL_PROJECT_PREFIX}/bin/" DESTINATION "bin" FILES_MATCHING PATTERN "MAPUtilities*") install(DIRECTORY "${MITK_EXTERNAL_PROJECT_PREFIX}/bin/" DESTINATION "bin" FILES_MATCHING PATTERN "MAPAlgorithms*") endif() + +if(MITK_USE_BetData) + install(DIRECTORY "${BetData_DIR}" + DESTINATION "bin" + FILES_MATCHING PATTERN "*") +endif() diff --git a/CMakeExternals/BetData.cmake b/CMakeExternals/BetData.cmake new file mode 100644 index 0000000000..9a399c026a --- /dev/null +++ b/CMakeExternals/BetData.cmake @@ -0,0 +1,36 @@ +#----------------------------------------------------------------------------- +# Brain Extraction Data +#----------------------------------------------------------------------------- + +if(MITK_USE_BetData) + + +# Sanity checks +if(DEFINED BetData_DIR AND NOT EXISTS ${BetData_DIR}) + message(FATAL_ERROR "BetData_DIR variable is defined but corresponds to non-existing directory") +endif() + +set(proj BetData) +set(proj_DEPENDENCIES) +set(BetData_DEPENDS ${proj}) + +if(NOT DEFINED BetData_DIR) + + ExternalProject_Add(${proj} + URL http://mitk.org/download/data/MITK-BET-20171220.tar.gz + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + DEPENDS ${proj_DEPENDENCIES} + ) + + set(BetData_DIR ${ep_prefix}/src/${proj}/) + configure_file(${CMAKE_CURRENT_LIST_DIR}/${proj}.h.in ${CMAKE_BINARY_DIR}/${proj}.h) + +else() + + mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") +endif() + +endif() diff --git a/CMakeExternals/BetData.h.in b/CMakeExternals/BetData.h.in new file mode 100644 index 0000000000..2aa9bba540 --- /dev/null +++ b/CMakeExternals/BetData.h.in @@ -0,0 +1,22 @@ +/*=================================================================== + +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 BetData_h +#define BetData_h + +#define BetData_DIR "@BetData_DIR@" + +#endif diff --git a/CMakeExternals/ExternalProjectList.cmake b/CMakeExternals/ExternalProjectList.cmake index 531b51119a..05e6d4c3b6 100644 --- a/CMakeExternals/ExternalProjectList.cmake +++ b/CMakeExternals/ExternalProjectList.cmake @@ -1,44 +1,45 @@ mitkFunctionAddExternalProject(NAME Poco ON COMPONENTS Foundation Net Util XML Zip) mitkFunctionAddExternalProject(NAME DCMTK ON DOC "EXPERIMENTAL, superbuild only: Use DCMTK in MITK") mitkFunctionAddExternalProject(NAME OpenIGTLink OFF) mitkFunctionAddExternalProject(NAME tinyxml ON ADVANCED) mitkFunctionAddExternalProject(NAME GDCM ON ADVANCED) mitkFunctionAddExternalProject(NAME GLUT OFF ADVANCED) mitkFunctionAddExternalProject(NAME Raptor2 OFF ADVANCED) mitkFunctionAddExternalProject(NAME Eigen ON ADVANCED DOC "Use the Eigen library") mitkFunctionAddExternalProject(NAME ANN ON ADVANCED DOC "Use Approximate Nearest Neighbor Library") mitkFunctionAddExternalProject(NAME CppUnit ON ADVANCED DOC "Use CppUnit for unit tests") mitkFunctionAddExternalProject(NAME PCRE OFF ADVANCED NO_PACKAGE) mitkFunctionAddExternalProject(NAME ZLIB OFF ADVANCED NO_PACKAGE NO_CACHE) mitkFunctionAddExternalProject(NAME HDF5 ON ADVANCED) +mitkFunctionAddExternalProject(NAME BetData OFF ADVANCED) # ----------------------------------------- # The following external projects must be # ordered according to their # inter-dependencies mitkFunctionAddExternalProject(NAME SWIG OFF ADVANCED NO_PACKAGE DEPENDS PCRE) mitkFunctionAddExternalProject(NAME Python OFF NO_PACKAGE DEPENDS SWIG DOC "Use Python wrapping in MITK") mitkFunctionAddExternalProject(NAME Numpy OFF ADVANCED NO_PACKAGE) mitkFunctionAddExternalProject(NAME OpenCV OFF) mitkFunctionAddExternalProject(NAME Vigra OFF DEPENDS HDF5) # These are "hard" dependencies and always set to ON mitkFunctionAddExternalProject(NAME ITK ON NO_CACHE DEPENDS HDF5) mitkFunctionAddExternalProject(NAME VTK ON NO_CACHE) mitkFunctionAddExternalProject(NAME Boost ON NO_CACHE) mitkFunctionAddExternalProject(NAME SimpleITK OFF DEPENDS ITK GDCM SWIG) mitkFunctionAddExternalProject(NAME ACVD OFF DOC "Use Approximated Centroidal Voronoi Diagrams") mitkFunctionAddExternalProject(NAME CTK ON DEPENDS Qt5 DCMTK DOC "Use CTK in MITK") mitkFunctionAddExternalProject(NAME Rasqal OFF DEPENDS Raptor2 PCRE ADVANCED) mitkFunctionAddExternalProject(NAME Redland OFF DEPENDS Rasqal DOC "Use the Redland RDF library") mitkFunctionAddExternalProject(NAME DCMQI OFF DEPENDS DCMTK ITK DOC "Use dcmqi in MITK") mitkFunctionAddExternalProject(NAME MatchPoint OFF ADVANCED DEPENDS ITK DOC "Use the MatchPoint translation image registration library") if(MITK_USE_Qt5) mitkFunctionAddExternalProject(NAME Qwt ON ADVANCED DEPENDS Qt5) endif() diff --git a/CMakeExternals/MITKData.cmake b/CMakeExternals/MITKData.cmake index 71a0996f06..1f74c21d84 100644 --- a/CMakeExternals/MITKData.cmake +++ b/CMakeExternals/MITKData.cmake @@ -1,39 +1,39 @@ #----------------------------------------------------------------------------- # MITK Data #----------------------------------------------------------------------------- # Sanity checks if(DEFINED MITK_DATA_DIR AND NOT EXISTS ${MITK_DATA_DIR}) message(FATAL_ERROR "MITK_DATA_DIR variable is defined but corresponds to non-existing directory") endif() set(proj MITK-Data) set(proj_DEPENDENCIES) set(MITK-Data_DEPENDS ${proj}) if(BUILD_TESTING) - set(revision_tag 91a6bd5e) # first 8 characters of hash-tag + set(revision_tag 6001ab9c) # first 8 characters of hash-tag # ^^^^^^^^ these are just to check correct length of hash part ExternalProject_Add(${proj} SOURCE_DIR ${proj} # GIT_REPOSITORY https://phabricator.mitk.org/diffusion/MD/mitk-data.git # GIT_TAG ${revision_tag} URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/mitk-data_${revision_tag}.tar.gz UPDATE_COMMAND "" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS ${proj_DEPENDENCIES} ) set(MITK_DATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif(BUILD_TESTING) diff --git a/Modules/Annotation/include/mitkLogoAnnotation.h b/Modules/Annotation/include/mitkLogoAnnotation.h index a065cb0725..53d2042983 100644 --- a/Modules/Annotation/include/mitkLogoAnnotation.h +++ b/Modules/Annotation/include/mitkLogoAnnotation.h @@ -1,103 +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. ===================================================================*/ #ifndef LOGOAnnotation_H #define LOGOAnnotation_H #include "MitkAnnotationExports.h" #include #include #include class mitkVtkLogoRepresentation; class vtkImageData; class vtkImageReader2Factory; class vtkImageImport; namespace mitk { /** \brief Displays a logo on the renderwindow */ class MITKANNOTATION_EXPORT LogoAnnotation : public mitk::VtkAnnotation { public: class LocalStorage : public mitk::Annotation::BaseLocalStorage { public: /** \brief Actor of a 2D render window. */ vtkSmartPointer m_LogoImage; vtkSmartPointer m_LogoRep; /** \brief Timestamp of last update of stored data. */ itk::TimeStamp m_LastUpdateTime; /** \brief Default constructor of the local storage. */ LocalStorage(); /** \brief Default deconstructor of the local storage. */ ~LocalStorage(); }; mitkClassMacro(LogoAnnotation, mitk::VtkAnnotation); itkFactorylessNewMacro(Self) itkCloneMacro(Self) vtkSmartPointer m_readerFactory; void SetLogoImage(vtkSmartPointer logo); void SetLogoImagePath(std::string text); std::string GetLogoImagePath() const; + void LoadLogoImageFromPath(); /** \brief The relative offset to the corner position */ void SetOffsetVector(const Point2D &OffsetVector); Point2D GetOffsetVector() const; /** \brief The corner where the logo is displayed. 0 = Bottom left 1 = Bottom right 2 = Top right 3 = Top left 4 = Center*/ void SetCornerPosition(const int &corner); int GetCornerPosition() const; void SetRelativeSize(const float &size); float GetRelativeSize() const; protected: /** \brief The LocalStorageHandler holds all LocalStorages for the render windows. */ mutable mitk::LocalStorageHandler m_LSH; vtkProp *GetVtkProp(BaseRenderer *renderer) const override; void UpdateVtkAnnotation(mitk::BaseRenderer *renderer) override; /** \brief explicit constructor which disallows implicit conversions */ explicit LogoAnnotation(); /** \brief virtual destructor in order to derive from this class */ ~LogoAnnotation() override; private: vtkSmartPointer m_UpdatedLogoImage; vtkSmartPointer m_VtkImageImport; /** \brief copy constructor */ LogoAnnotation(const LogoAnnotation &); /** \brief assignment operator */ LogoAnnotation &operator=(const LogoAnnotation &); }; } // namespace mitk #endif // LOGOAnnotation_H diff --git a/Modules/Annotation/src/mitkLogoAnnotation.cpp b/Modules/Annotation/src/mitkLogoAnnotation.cpp index 1b6a161cbd..13e615ec88 100644 --- a/Modules/Annotation/src/mitkLogoAnnotation.cpp +++ b/Modules/Annotation/src/mitkLogoAnnotation.cpp @@ -1,158 +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 "mitkLogoAnnotation.h" +#include #include "vtkUnicodeString.h" #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include mitk::LogoAnnotation::LogoAnnotation() { m_readerFactory = vtkSmartPointer::New(); mitk::Point2D offset; offset.Fill(0.03); SetOffsetVector(offset); SetRelativeSize(0.2); SetLogoImagePath("mbiLogo"); SetCornerPosition(3); m_VtkImageImport = vtkSmartPointer::New(); m_UpdatedLogoImage = vtkSmartPointer::New(); } mitk::LogoAnnotation::~LogoAnnotation() { for (BaseRenderer *renderer : m_LSH.GetRegisteredBaseRenderer()) { if (renderer) { this->RemoveFromBaseRenderer(renderer); } } } mitk::LogoAnnotation::LocalStorage::~LocalStorage() { } mitk::LogoAnnotation::LocalStorage::LocalStorage() { m_LogoRep = vtkSmartPointer::New(); } void mitk::LogoAnnotation::UpdateVtkAnnotation(mitk::BaseRenderer *renderer) { LocalStorage *ls = this->m_LSH.GetLocalStorage(renderer); if (ls->IsGenerateDataRequired(renderer, this)) { ls->m_LogoImage = m_UpdatedLogoImage; ls->m_LogoRep->SetImage(ls->m_LogoImage); ls->m_LogoRep->SetDragable(false); ls->m_LogoRep->SetMoving(false); ls->m_LogoRep->SetPickable(false); ls->m_LogoRep->SetShowBorder(true); ls->m_LogoRep->SetRenderer(renderer->GetVtkRenderer()); float size = GetRelativeSize(); ls->m_LogoRep->SetPosition2(size, size); int corner = GetCornerPosition(); ls->m_LogoRep->SetCornerPosition(corner); mitk::Point2D offset = GetOffsetVector(); ls->m_LogoRep->SetPosition(offset[0], offset[1]); float opacity = 1.0; GetOpacity(opacity); ls->m_LogoRep->GetImageProperty()->SetOpacity(opacity); ls->m_LogoRep->BuildRepresentation(); ls->UpdateGenerateDataTime(); } } void mitk::LogoAnnotation::SetLogoImagePath(std::string path) { SetStringProperty("Annotation.LogoImagePath", path.c_str()); Modified(); } +void mitk::LogoAnnotation::LoadLogoImageFromPath() +{ + auto imageNodeVector = mitk::IOUtil::Load(this->GetLogoImagePath()); + + if (imageNodeVector.size() == 1) + { + mitk::Image::Pointer image = dynamic_cast(imageNodeVector.front().GetPointer()); + + if (image.IsNotNull()) + { + vtkNew flip; + flip->SetInputData(image->GetVtkImageData()); + flip->SetFilteredAxis(1); + flip->Update(); + m_UpdatedLogoImage->DeepCopy(flip->GetOutput()); + Modified(); + } + } +} + void mitk::LogoAnnotation::SetLogoImage(vtkSmartPointer logo) { m_UpdatedLogoImage = logo; Modified(); } std::string mitk::LogoAnnotation::GetLogoImagePath() const { std::string path; GetPropertyList()->GetStringProperty("Annotation.LogoImagePath", path); return path; } void mitk::LogoAnnotation::SetOffsetVector(const Point2D &OffsetVector) { mitk::Point2dProperty::Pointer OffsetVectorProperty = mitk::Point2dProperty::New(OffsetVector); SetProperty("Annotation.OffsetVector", OffsetVectorProperty.GetPointer()); Modified(); } mitk::Point2D mitk::LogoAnnotation::GetOffsetVector() const { mitk::Point2D OffsetVector; OffsetVector.Fill(0); GetPropertyValue("Annotation.OffsetVector", OffsetVector); return OffsetVector; } void mitk::LogoAnnotation::SetCornerPosition(const int &corner) { SetIntProperty("Annotation.CornerPosition", corner); Modified(); } int mitk::LogoAnnotation::GetCornerPosition() const { int corner = 0; GetIntProperty("Annotation.CornerPosition", corner); return corner; } void mitk::LogoAnnotation::SetRelativeSize(const float &size) { SetFloatProperty("Annotation.RelativeSize", size); Modified(); } float mitk::LogoAnnotation::GetRelativeSize() const { float size = 0; GetFloatProperty("Annotation.RelativeSize", size); return size; } vtkProp *mitk::LogoAnnotation::GetVtkProp(BaseRenderer *renderer) const { LocalStorage *ls = this->m_LSH.GetLocalStorage(renderer); return ls->m_LogoRep; } diff --git a/Modules/Annotation/test/mitkLogoAnnotationTest.cpp b/Modules/Annotation/test/mitkLogoAnnotationTest.cpp index 51f541c416..9c6bd4bbc1 100644 --- a/Modules/Annotation/test/mitkLogoAnnotationTest.cpp +++ b/Modules/Annotation/test/mitkLogoAnnotationTest.cpp @@ -1,138 +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. ===================================================================*/ // MITK #include #include #include #include #include #include class mitkLogoAnnotationTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLogoAnnotationTestSuite); MITK_TEST(RenderMbiLogo); MITK_TEST(RenderLogo); CPPUNIT_TEST_SUITE_END(); private: /** Members used inside the different test methods. All members are initialized via setUp().*/ mitk::RenderingTestHelper m_RenderingTestHelper; std::vector m_CommandlineArgs; std::string m_PathToBall; std::string m_PathToImage; std::string m_PathToLogo; std::string m_PathToMitkLogo; std::string m_ReferenceImagePath; public: /** * @brief mitkManualPlacementAnnotationRendererTestSuite Because the RenderingTestHelper does not have an * empty default constructor, we need this constructor to initialize the helper with a * resolution. */ mitkLogoAnnotationTestSuite() : m_RenderingTestHelper(300, 300) {} /** * @brief Setup Initialize a fresh rendering test helper and a vector of strings * to simulate commandline arguments for vtkTesting::Test. */ void setUp() { m_RenderingTestHelper = mitk::RenderingTestHelper(300, 300); m_PathToBall = GetTestDataFilePath("ball.stl"); m_PathToImage = GetTestDataFilePath("Pic3D.nrrd"); m_PathToLogo = GetTestDataFilePath("RenderingTestData/texture.jpg"); m_PathToMitkLogo = GetTestDataFilePath("RenderingTestData/defaultWatermark.png"); m_ReferenceImagePath = "RenderingTestData/ReferenceScreenshots/Annotation/"; // Build a command line for the vtkTesting::Test method. // See VTK documentation and RenderingTestHelper for more information. // Use the following command line option to save the difference image // and the test image in some tmp folder // m_CommandlineArgs.push_back("-T"); // m_CommandlineArgs.push_back("/path/to/save/tmp/difference/images/"); m_CommandlineArgs.push_back("-V"); } void tearDown() {} void RenderMbiLogo() { mitk::DataNode::Pointer ballnode = mitk::DataNode::New(); ballnode->SetData(mitk::IOUtil::Load(m_PathToBall)[0]); m_RenderingTestHelper.AddNodeToStorage(ballnode); mitk::DataNode::Pointer imagenode = mitk::DataNode::New(); imagenode->SetData(mitk::IOUtil::Load(m_PathToImage)[0]); m_RenderingTestHelper.AddNodeToStorage(imagenode); std::string refImagePath = GetTestDataFilePath(m_ReferenceImagePath + "LogoAnnotation_mitkLogo.png"); // reference screenshot for this test m_CommandlineArgs.push_back(refImagePath); // Convert vector of strings to argc/argv mitk::RenderingTestHelper::ArgcHelperClass arg(m_CommandlineArgs); m_RenderingTestHelper.SetViewDirection(mitk::SliceNavigationController::Sagittal); mitk::LogoAnnotation::Pointer logoAnnotation = mitk::LogoAnnotation::New(); logoAnnotation->SetLogoImagePath(m_PathToMitkLogo); + logoAnnotation->LoadLogoImageFromPath(); + logoAnnotation->SetOpacity(0.5); + mitk::Point2D offset; + offset.Fill(0.03); + logoAnnotation->SetOffsetVector(offset); + logoAnnotation->SetRelativeSize(0.5); + logoAnnotation->SetCornerPosition(1); mitk::BaseRenderer *renderer = mitk::BaseRenderer::GetInstance(m_RenderingTestHelper.GetVtkRenderWindow()); mitk::ManualPlacementAnnotationRenderer::AddAnnotation(logoAnnotation.GetPointer(), renderer); m_RenderingTestHelper.Render(); // m_RenderingTestHelper.SaveReferenceScreenShot(refImagePath); m_RenderingTestHelper.SetAutomaticallyCloseRenderWindow(true); CPPUNIT_ASSERT(m_RenderingTestHelper.CompareRenderWindowAgainstReference(arg.GetArgc(), arg.GetArgv()) == true); } void RenderLogo() { mitk::DataNode::Pointer ballnode = mitk::DataNode::New(); ballnode->SetData(mitk::IOUtil::Load(m_PathToBall)[0]); m_RenderingTestHelper.AddNodeToStorage(ballnode); mitk::DataNode::Pointer imagenode = mitk::DataNode::New(); imagenode->SetData(mitk::IOUtil::Load(m_PathToImage)[0]); m_RenderingTestHelper.AddNodeToStorage(imagenode); std::string refImagePath = GetTestDataFilePath(m_ReferenceImagePath + "LogoAnnotation.png"); // reference screenshot for this test m_CommandlineArgs.push_back(refImagePath); // Convert vector of strings to argc/argv mitk::RenderingTestHelper::ArgcHelperClass arg(m_CommandlineArgs); m_RenderingTestHelper.SetViewDirection(mitk::SliceNavigationController::Sagittal); mitk::LogoAnnotation::Pointer logoAnnotation = mitk::LogoAnnotation::New(); logoAnnotation->SetLogoImagePath(m_PathToLogo); + logoAnnotation->LoadLogoImageFromPath(); mitk::BaseRenderer *renderer = mitk::BaseRenderer::GetInstance(m_RenderingTestHelper.GetVtkRenderWindow()); mitk::ManualPlacementAnnotationRenderer::AddAnnotation(logoAnnotation.GetPointer(), renderer); m_RenderingTestHelper.Render(); // m_RenderingTestHelper.SaveReferenceScreenShot(refImagePath); m_RenderingTestHelper.SetAutomaticallyCloseRenderWindow(true); CPPUNIT_ASSERT(m_RenderingTestHelper.CompareRenderWindowAgainstReference(arg.GetArgc(), arg.GetArgv()) == true); } }; MITK_TEST_SUITE_REGISTRATION(mitkLogoAnnotation) diff --git a/Modules/Chart/include/QmitkChartData.h b/Modules/Chart/include/QmitkChartData.h index 3d26eefcf5..66c9d2d3b3 100644 --- a/Modules/Chart/include/QmitkChartData.h +++ b/Modules/Chart/include/QmitkChartData.h @@ -1,92 +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. ===================================================================*/ #ifndef QmitkC3Data_h #define QmitkC3Data_h #include #include /** \brief This class holds the relevant properties for the chart generation with C3 such as labels and diagram type. * It is derived from QObject, because we need Q_PROPERTIES to send Data via QWebChannel to JavaScript. * \sa The actual data for the chart generation is in QmitkC3xyData! */ class QmitkChartData : public QObject { Q_OBJECT Q_PROPERTY(QVariant m_xAxisLabel READ GetXAxisLabel WRITE SetXAxisLabel NOTIFY SignalXAxisLabelChanged); Q_PROPERTY(QVariant m_yAxisLabel READ GetYAxisLabel WRITE SetYAxisLabel NOTIFY SignalYAxisLabelChanged); Q_PROPERTY(QVariant m_chartTitle READ GetTitle WRITE SetTitle NOTIFY SignalTitleChanged); Q_PROPERTY(QVariant m_LegendPosition READ GetLegendPosition WRITE SetLegendPosition NOTIFY SignalLegendPositionChanged); + Q_PROPERTY(QVariant m_ShowLegend READ GetShowLegend WRITE SetShowLegend NOTIFY SignalShowLegendChanged); Q_PROPERTY(QVariant m_YAxisScale READ GetYAxisScale WRITE SetYAxisScale NOTIFY SignalYAxisScaleChanged); Q_PROPERTY(QVariant m_ShowSubchart READ GetShowSubchart WRITE SetShowSubchart NOTIFY SignalShowSubchartChanged); Q_PROPERTY(QVariant m_UsePercentageInPieChart READ GetUsePercentageInPieChart WRITE SetUsePercentageInPieChart NOTIFY SignalUsePercentageInPieChartChanged); Q_PROPERTY(QVariant m_DataPointSize READ GetDataPointSize WRITE SetDataPointSize NOTIFY SignalDataPointSizeChanged); public: QmitkChartData(); void SetAppearance(bool showSubChart = true, bool usePercentageInPieChart = false); Q_INVOKABLE QVariant GetXAxisLabel() const { return m_xAxisLabel; }; Q_INVOKABLE void SetXAxisLabel(const QVariant& label) { m_xAxisLabel = label; emit SignalXAxisLabelChanged(label); }; Q_INVOKABLE QVariant GetYAxisLabel() const { return m_yAxisLabel; }; Q_INVOKABLE void SetYAxisLabel(const QVariant& label) { m_yAxisLabel = label; emit SignalYAxisLabelChanged(label); }; Q_INVOKABLE QVariant GetTitle() const { return m_chartTitle; }; Q_INVOKABLE void SetTitle(const QVariant& title) { m_chartTitle = title; emit SignalTitleChanged(title); }; Q_INVOKABLE QVariant GetLegendPosition() const { return m_LegendPosition; }; Q_INVOKABLE void SetLegendPosition(const QVariant& legendPosition) { m_LegendPosition = legendPosition; emit SignalLegendPositionChanged(legendPosition); }; + Q_INVOKABLE QVariant GetShowLegend() const { return m_ShowLegend; }; + Q_INVOKABLE void SetShowLegend(const QVariant& show) { m_ShowLegend = show; emit SignalShowLegendChanged(show); }; + Q_INVOKABLE QVariant GetYAxisScale() const { return m_YAxisScale; }; Q_INVOKABLE void SetYAxisScale(const QVariant& YAxisScale) { m_YAxisScale = YAxisScale; emit SignalYAxisScaleChanged(YAxisScale); }; Q_INVOKABLE QVariant GetShowSubchart() const { return m_ShowSubchart; }; Q_INVOKABLE void SetShowSubchart(const QVariant& showSubchart) { m_ShowSubchart = showSubchart; emit SignalShowSubchartChanged(showSubchart); }; Q_INVOKABLE QVariant GetUsePercentageInPieChart() const { return m_UsePercentageInPieChart; }; Q_INVOKABLE void SetUsePercentageInPieChart(const QVariant& usePercentageInPieChart) { m_UsePercentageInPieChart = usePercentageInPieChart; emit SignalUsePercentageInPieChartChanged(usePercentageInPieChart); }; Q_INVOKABLE QVariant GetDataPointSize() const { return m_DataPointSize; }; Q_INVOKABLE void SetDataPointSize(const QVariant& showDataPoints) {if (showDataPoints > 0 ) { m_DataPointSize = 3; } else { m_DataPointSize = 0; } emit SignalDataPointSizeChanged(showDataPoints); }; signals: void SignalYAxisLabelChanged(const QVariant label); void SignalXAxisLabelChanged(const QVariant label); void SignalLegendPositionChanged(const QVariant legendPosition); + void SignalShowLegendChanged(const QVariant show); void SignalYAxisScaleChanged(const QVariant YAxisScale); void SignalTitleChanged(const QVariant title); void SignalShowSubchartChanged(const QVariant showSubchart); void SignalUsePercentageInPieChartChanged(const QVariant usePercentageInPieChart); void SignalDataPointSizeChanged(const QVariant showDataPoints); private: QVariant m_xAxisLabel; QVariant m_yAxisLabel; QVariant m_chartTitle; + QVariant m_ShowLegend = true; QVariant m_LegendPosition; QVariant m_ShowSubchart; QVariant m_YAxisScale; QVariant m_UsePercentageInPieChart; QVariant m_numberDatasets; QVariant m_DataPointSize = 0; }; #endif //QmitkC3Data_h diff --git a/Modules/Chart/include/QmitkChartWidget.h b/Modules/Chart/include/QmitkChartWidget.h index e79ce6d4c2..7e5295d184 100644 --- a/Modules/Chart/include/QmitkChartWidget.h +++ b/Modules/Chart/include/QmitkChartWidget.h @@ -1,196 +1,199 @@ /*=================================================================== 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 /*! \brief QmitkChartWidget is a widget to display various charts based on the javascript chart library C3js. Currently, bar charts, line charts and pie charts are supported. * \details Data is added via AddData1D() or AddData2D().\n * There can be multiple charts (of the same type) created by calling AddData1D or AddData2D multiple times.\n\n * The following chart types are supported: * * line chart: http://c3js.org/samples/simple_multiple.html * * bar chart: http://c3js.org/samples/chart_bar.html * * spline chart: http://c3js.org/samples/chart_spline.html * * pie chart: http://c3js.org/samples/chart_pie.html * \n Technical details: The javascript code is embedded in a QWebEngineView. The actual js code is implemented in resource\Chart.js. * \sa http://c3js.org for further information about the used javaScript library. * \warning Pie is significantly different than the other types. Here, the data given by AddData1D is summed. Each entry represents a different category. * \ingroup Modules/Chart */ class MITKCHART_EXPORT QmitkChartWidget : public QWidget { Q_OBJECT public: /*! * \brief enum of diagram types. Supported are bar, line, spline (a smoothed line) and pie. */ enum class ChartType { bar, /*!< bar chart, see http://c3js.org/samples/chart_bar.html */ line, /*!< line chart, see http://c3js.org/samples/simple_multiple.html */ spline, /*!< spline chart (smoothed line chart), see http://c3js.org/samples/chart_spline.html */ pie, /*!< pie chart, see http://c3js.org/samples/chart_pie.html*/ area, /*!< area chart, see http://c3js.org/samples/chart_area.html*/ area_spline /*!< area-spline chart, see http://c3js.org/samples/chart_area.html*/ }; enum class ChartStyle { darkstyle, lightstyle }; enum class LineStyle { solid, dashed }; enum class AxisScale { linear, log }; /*! * \brief enum of legend position. Supported are bottom, right, inset. * See http://c3js.org/reference.html#legend-position */ enum class LegendPosition { bottom, right, inset }; explicit QmitkChartWidget(QWidget* parent = nullptr); ~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 type 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. */ void AddData1D(const std::vector& data1D, const std::string& label, ChartType type = 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 type 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. */ void AddData2D(const std::map& data2D, const std::string& label, ChartType type = 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 the 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). * Either define all data entries with a color or none. If a mixed approach is used, different data entries could have the same color. * 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 linestyle is solid. * 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); void SetYAxisScale(AxisScale scale); void SetXAxisLabel(const std::string& label); std::string GetXAxisLabel() const; void SetYAxisLabel(const std::string& label); std::string GetYAxisLabel() const; void SetTitle(const std::string &title); std::string GetTitle() const; /*! * \brief sets the chart type for a data entry * \details for available types, see ChartType * If an unknown label is given, nothing happens. * \sa DiagramType for available types */ void SetChartType(const std::string& label, ChartType type); void SetLegendPosition(LegendPosition position); LegendPosition GetLegendPosition() const; + void SetShowLegend(bool show); + bool GetShowLegend() const; + /*! * \brief Changes the chart type for all data entries and reloads the chart */ void SetChartTypeForAllDataAndReload(ChartType type); /*! * \brief Displays the chart in the widget * \param showSubChart if a subchart is displayed inside the widget or not (see http://c3js.org/samples/options_subchart.html). * \exception if no data has been provided (\sa AddData1D AddData2D) */ void Show(bool showSubChart=false); /*! * \brief Displays the dataPoints or not * \param showDataPoints if dataPoints are displayed inside the widget or not. */ void SetShowDataPoints(bool showDataPoints); bool GetShowDataPoints() const; /*! * \brief Clears all data inside and resets the widget. */ void Clear(); /*! * \brief Changes the theme of the widget. */ void SetTheme(ChartStyle themeEnabled); /*! * \brief Reloads the chart in the widget * \details reloading may be needed to display added data in an existing chart * \param showSubChart if a subchart is displayed inside the widget or not. */ void Reload(bool showSubChart); private: class Impl; Impl* m_Impl; public slots: void OnLoadFinished(bool isLoadSuccessfull); signals: void PageSuccessfullyLoaded(); }; #endif diff --git a/Modules/Chart/resource/Chart.js b/Modules/Chart/resource/Chart.js index 1960572437..437c439153 100644 --- a/Modules/Chart/resource/Chart.js +++ b/Modules/Chart/resource/Chart.js @@ -1,236 +1,236 @@ //Based on C3.js (http://c3js.org). See Website for examples! document.body.style.backgroundColor = 'rgb(240, 240, 240)'; var minHeight = 255; var chart; GenerateChart() var chartData; var xValues=[]; var yValues=[]; var xs = {}; var dataColors = {}; var chartTypes = {}; var lineStyle = {}; //Is executed when js is loaded first. //Extracts relevant information from chartData in variables window.onload = function() { new QWebChannel(qt.webChannelTransport, function(channel) { chartData = channel.objects.chartData; var count = 0; for(var propertyName in channel.objects) { if (propertyName != 'chartData'){ var xDataTemp = channel.objects[propertyName].m_XData var yDataTemp = channel.objects[propertyName].m_YData var dataLabelsTemp; //add label to x array xDataTemp.unshift('x'+count.toString()) dataLabelsTemp = channel.objects[propertyName].m_Label xs[dataLabelsTemp] = 'x'+count.toString() xDataTemp.push(null); //append null value, to make sure the last tick on x-axis is displayed correctly yDataTemp.unshift(dataLabelsTemp) yDataTemp.push(null); //append null value, to make sure the last tick on y-axis is displayed correctly xValues[count] = xDataTemp yValues[count] = yDataTemp dataColors[dataLabelsTemp] = channel.objects[propertyName].m_Color chartTypes[dataLabelsTemp] = channel.objects[propertyName].m_ChartType if (channel.objects[propertyName].m_LineStyleName=="solid"){ lineStyle[dataLabelsTemp]= '' } else { lineStyle[dataLabelsTemp]= [{'style':'dashed'}] } count++; } } setupChart(chartData) }); } //This is necessary to resize the chart, after the size of the parent changed window.onresize = function () { var size = window.innerHeight-(window.innerHeight/100*10); //subtract 5% of height to hide vertical scrool bar if (size < minHeight) { size = minHeight; } chart.resize({ height: size, }); } function ReloadChart(showSubchart) { chartData.m_ShowSubchart = showSubchart; setupChart(chartData); } function setupChart(chartData) { window.onresize(); GenerateChart(chartData) chart.unload(); //unload data before loading new data //for multiple xy line chart, see http://c3js.org/samples/simple_xy_multiple.html var columns = []; for (var i in xValues){ columns.push(xValues[i]) } for (var i in yValues){ columns.push(yValues[i]) } chart.load({ xs: xs, columns: columns }); } //Transformation between different chart types //takes the name of the chart type as a parameter //called by QmitkC3jsWidget function transformView(TransformTo) { chart.transform(TransformTo); }; function changeTheme(color) { link = document.getElementsByTagName("link")[0]; if (color == 'dark') { link.href = "Chart_dark.css"; } else { link.href = "Chart.css"; } }; //Here, the chart magic takes place. C3js is called function GenerateChart(chartData) { if (chartData === undefined) { chartData = {} } //adaption for bar ratio indepenend of amount of data points //otherwise, bars could be covered. var barRatio; try { barRatio = 0.8*Math.exp(-0.015*xValues[0].length); } catch (err){ barRatio=0.42 } var formatCharacter; if (chartData.m_UsePercentageInPieChart==true){ formatCharacter = '%' } else{ formatCharacter = '.1f' } chart = c3.generate({ title:{ text: chartData.m_diagramTitle, position: 'top-center' }, data: { xs: {}, //use first "column" as x axis values columns: [], //initialize empty. Data will be loaded in function setupChart(chartData) types: chartTypes, selection: { enabled: false, multiple: false, }, colors: dataColors, regions: lineStyle, }, legend: { position: chartData.m_LegendPosition, - show: true + show: chartData.m_ShowLegend }, grid: { y: { lines: [{value:0}] //Draws a horizontal line at y=0 } }, bar: { width: { ratio: barRatio } }, pie:{ label: { format: function (value, ratio, id) { if (chartData.m_UsePercentageInPieChart==true){ return d3.format('%') (ratio); } else{ return value; } } } }, zoom: { enabled: true, }, subchart: { show: chartData.m_ShowSubchart //Shows a subchart that shows the region the primary chart is zoomed in to by overlay. }, axis: { x: { tick: { multiline: false, fit: false, //to make more x labels appear on zoom centered: true, format: d3.format(".1f"), }, label: { text: chartData.m_xAxisLabel, position: 'outer-center' } }, y: { tick: { format: d3.format(formatCharacter) }, label: { text: chartData.m_yAxisLabel, position: 'outer-middle' }, scale: { name: chartData.m_YAxisScale } } }, tooltip: { format: { title: function (x) { return chartData.m_xAxisLabel + ': ' + d3.format(".2f")(x)} }, }, //Style data points in linegraph point: { r: chartData.m_DataPointSize, focus: { expand: { r: chartData.m_DataPointSize + 2 } } }, }); } diff --git a/Modules/Chart/src/QmitkChartWidget.cpp b/Modules/Chart/src/QmitkChartWidget.cpp index bc9223909d..3b6bfc2e26 100644 --- a/Modules/Chart/src/QmitkChartWidget.cpp +++ b/Modules/Chart/src/QmitkChartWidget.cpp @@ -1,516 +1,537 @@ /*=================================================================== 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 "mitkExceptionMacro.h" 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); std::string GetXAxisLabel() const; void SetYAxisLabel(const std::string& label); std::string GetYAxisLabel() const; void SetTitle(const std::string &title); std::string GetTitle() const; void SetLegendPosition(LegendPosition position); LegendPosition GetLegendPosition() const; std::string GetLegendPositionAsString() const; + void SetShowLegend(bool show); + bool GetShowLegend() const; + void SetShowDataPoints(bool showDataPoints = false); bool GetShowDataPoints() const; void SetChartType(const std::string& label, QmitkChartWidget::ChartType chartType); std::string ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const; void ClearJavaScriptChart(); void InitializeJavaScriptChart(); void CallJavaScriptFuntion(const QString& command); QmitkChartData* GetC3Data() const; std::vector* GetC3xyData() const; private: std::string GetUniqueLabelName(const QList& labelList, const std::string& label) const; QmitkChartxyData* GetDataElementByLabel(const std::vector* c3xyData, const std::string& label) const; QList GetDataLabels(const std::vector* c3xyData) const; QWebChannel* m_WebChannel; QWebEngineView* m_WebEngineView; QmitkChartData * m_C3Data; std::vector * m_C3xyData; std::map m_ChartTypeToName; std::map m_LegendPositionToName; std::map m_LineStyleToName; std::map m_AxisScaleToName; }; 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); //Set the webengineview to an initial empty page. The actual chart will be loaded once the data is calculated. m_WebEngineView->setUrl(QUrl(QStringLiteral("qrc:///C3js/empty.html"))); m_WebEngineView->page()->setWebChannel(m_WebChannel); connect(m_WebEngineView, SIGNAL(loadFinished(bool)), parent, SLOT(OnLoadFinished(bool))); auto layout = new QGridLayout(parent); layout->setMargin(0); layout->addWidget(m_WebEngineView); parent->setLayout(layout); 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_LegendPositionToName.emplace(LegendPosition::bottom, "bottom"); m_LegendPositionToName.emplace(LegendPosition::right, "right"); m_LegendPositionToName.emplace(LegendPosition::inset, "inset"); m_LineStyleToName.emplace(LineStyle::solid, "solid"); m_LineStyleToName.emplace(LineStyle::dashed, "dashed"); m_AxisScaleToName.emplace(AxisScale::linear, ""); m_AxisScaleToName.emplace(AxisScale::log, "log"); m_C3Data = new QmitkChartData(); m_C3xyData = new std::vector(); } QmitkChartWidget::Impl::~Impl() { delete m_C3Data; qDeleteAll(*m_C3xyData); delete m_C3xyData; } 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++; } this->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(GetC3xyData()); auto uniqueLabel = GetUniqueLabelName(definedLabels, label); GetC3xyData()->push_back(new QmitkChartxyData(data2DConverted, QVariant(QString::fromStdString(uniqueLabel)), QVariant(QString::fromStdString(chartTypeName)))); } void QmitkChartWidget::Impl::RemoveData(const std::string& label) { for (std::vector::iterator iter = GetC3xyData()->begin(); iter != GetC3xyData()->end(); ++iter) { const auto &temp = *iter; if (temp->GetLabel().toString().toStdString() == label) { delete temp; GetC3xyData()->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); delete xyData; xyData = nullptr; } GetC3xyData()->clear(); } 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::SetColor(const std::string& label, const std::string& colorName) { auto element = GetDataElementByLabel(GetC3xyData(), 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(GetC3xyData(), label); //only has effect with chart type line if (element && element->GetChartType()==QVariant(QString::fromStdString(ConvertChartTypeToString(ChartType::line)))) { 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)); GetC3Data()->SetYAxisScale(QString::fromStdString(axisScaleName)); } QmitkChartxyData* QmitkChartWidget::Impl::GetDataElementByLabel(const std::vector* c3xyData, const std::string& label) const { for (auto element = c3xyData->begin(); element != c3xyData->end(); ++element) { if ((*element)->GetLabel().toString().toStdString() == label) { return *element; } } MITK_WARN << "label " << label << " not found in QmitkChartWidget"; return nullptr; } QList QmitkChartWidget::Impl::GetDataLabels(const std::vector* 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) { GetC3Data()->SetXAxisLabel(QString::fromStdString(label)); } std::string QmitkChartWidget::Impl::GetXAxisLabel() const { return GetC3Data()->GetXAxisLabel().toString().toStdString(); } void QmitkChartWidget::Impl::SetYAxisLabel(const std::string& label) { GetC3Data()->SetYAxisLabel(QString::fromStdString(label)); } std::string QmitkChartWidget::Impl::GetYAxisLabel() const { return GetC3Data()->GetYAxisLabel().toString().toStdString(); } void QmitkChartWidget::Impl::SetTitle(const std::string& title) { GetC3Data()->SetTitle(QString::fromStdString(title)); } std::string QmitkChartWidget::Impl::GetTitle() const { return GetC3Data()->GetTitle().toString().toStdString(); } void QmitkChartWidget::Impl::SetLegendPosition(QmitkChartWidget::LegendPosition legendPosition) { const std::string legendPositionName(m_LegendPositionToName.at(legendPosition)); GetC3Data()->SetLegendPosition(QString::fromStdString(legendPositionName)); } QmitkChartWidget::LegendPosition QmitkChartWidget::Impl::GetLegendPosition() const { for (const auto& aLegendPosition : m_LegendPositionToName) { if (aLegendPosition.second == GetLegendPositionAsString()) { return aLegendPosition.first; } } mitkThrow() << "can't find legend position!"; } std::string QmitkChartWidget::Impl::GetLegendPositionAsString() const { return GetC3Data()->GetLegendPosition().toString().toStdString(); } +void QmitkChartWidget::Impl::SetShowLegend(bool show) { + GetC3Data()->SetShowLegend(show); +} + +bool QmitkChartWidget::Impl::GetShowLegend() const { + return GetC3Data()->GetShowLegend().toBool(); +} + void QmitkChartWidget::Impl::SetShowDataPoints(bool showDataPoints) { if (showDataPoints == true) { GetC3Data()->SetDataPointSize(3); } else { GetC3Data()->SetDataPointSize(0); } } bool QmitkChartWidget::Impl::GetShowDataPoints() const{ int value = GetC3Data()->GetDataPointSize().toInt(); if (value > 0) { return true; } else { return false; } } void QmitkChartWidget::Impl::SetChartType(const std::string& label, QmitkChartWidget::ChartType chartType) { auto element = GetDataElementByLabel(GetC3xyData(), label); if (element) { const std::string chartTypeName(m_ChartTypeToName.at(chartType)); element->SetChartType(QVariant(QString::fromStdString(chartTypeName))); } } std::string QmitkChartWidget::Impl::ConvertChartTypeToString(QmitkChartWidget::ChartType chartType) const { return m_ChartTypeToName.at(chartType); } QmitkChartData* QmitkChartWidget::Impl::GetC3Data() const { return m_C3Data; } std::vector* QmitkChartWidget::Impl::GetC3xyData() const { return m_C3xyData; } void QmitkChartWidget::Impl::CallJavaScriptFuntion(const QString& command) { m_WebEngineView->page()->runJavaScript(command); } void QmitkChartWidget::Impl::ClearJavaScriptChart() { m_WebEngineView->setUrl(QUrl(QStringLiteral("qrc:///C3js/empty.html"))); } void QmitkChartWidget::Impl::InitializeJavaScriptChart() { 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); count++; } m_WebEngineView->load(QUrl(QStringLiteral("qrc:///C3js/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)) { } QmitkChartWidget::~QmitkChartWidget() { delete m_Impl; } 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); } std::string QmitkChartWidget::GetXAxisLabel() const { return m_Impl->GetXAxisLabel(); } void QmitkChartWidget::SetYAxisLabel(const std::string & label) { m_Impl->SetYAxisLabel(label); } std::string QmitkChartWidget::GetYAxisLabel() const { return m_Impl->GetYAxisLabel(); } void QmitkChartWidget::SetTitle(const std::string & title) { m_Impl->SetTitle(title); } std::string QmitkChartWidget::GetTitle() const { return m_Impl->GetTitle(); } void QmitkChartWidget::SetShowDataPoints(bool showDataPoints) { m_Impl->SetShowDataPoints(showDataPoints); } bool QmitkChartWidget::GetShowDataPoints() const { return m_Impl->GetShowDataPoints(); } void QmitkChartWidget::SetChartType(const std::string& label, ChartType type) { m_Impl->SetChartType(label, type); } void QmitkChartWidget::SetLegendPosition(LegendPosition position) { m_Impl->SetLegendPosition(position); } QmitkChartWidget::LegendPosition QmitkChartWidget::GetLegendPosition() const { return m_Impl->GetLegendPosition(); } +void QmitkChartWidget::SetShowLegend(bool show) +{ + m_Impl->SetShowLegend(show); +} + +bool QmitkChartWidget::GetShowLegend() const +{ + return m_Impl->GetShowLegend(); +} + void QmitkChartWidget::Show(bool showSubChart) { if (m_Impl->GetC3xyData()->empty()) { mitkThrow() << "no data available for display in chart"; } this->m_Impl->GetC3Data()->SetAppearance(showSubChart, m_Impl->GetC3xyData()->front()->GetChartType()== QVariant("pie")); m_Impl->InitializeJavaScriptChart(); } void QmitkChartWidget::Clear() { m_Impl->ClearData(); m_Impl->ClearJavaScriptChart(); } void QmitkChartWidget::OnLoadFinished(bool isLoadSuccessfull) { if(isLoadSuccessfull) { emit PageSuccessfullyLoaded(); } } void QmitkChartWidget::SetChartTypeForAllDataAndReload(ChartType type) { auto allData = this->m_Impl->GetC3xyData(); for (auto iterator = allData->begin(); iterator != allData->end(); ++iterator) { SetChartType((*iterator)->GetLabel().toString().toStdString(), type); } auto chartTypeName = m_Impl->ConvertChartTypeToString(type); const QString command = QString::fromStdString("transformView('" + chartTypeName + "')"); m_Impl->CallJavaScriptFuntion(command); } void QmitkChartWidget::SetTheme(ChartStyle themeEnabled) { QString command; if (themeEnabled == ChartStyle::darkstyle) { command = QString("changeTheme('dark')"); } else { command = QString("changeTheme('light')"); } m_Impl->CallJavaScriptFuntion(command); } void QmitkChartWidget::Reload(bool showSubChart) { QString subChartString; if (showSubChart) { subChartString = "true"; } else { subChartString = "false"; } const QString command = QString("ReloadChart(" + subChartString + ")"); m_Impl->CallJavaScriptFuntion(command); } diff --git a/Modules/DiffusionImaging/FiberTracking/CMakeLists.txt b/Modules/DiffusionImaging/FiberTracking/CMakeLists.txt index 3d2ef66df8..36f9917b3c 100644 --- a/Modules/DiffusionImaging/FiberTracking/CMakeLists.txt +++ b/Modules/DiffusionImaging/FiberTracking/CMakeLists.txt @@ -1,54 +1,52 @@ set(_module_deps MitkDiffusionCore MitkGraphAlgorithms MitkCLVigraRandomForest) mitk_check_module_dependencies( MODULES ${_module_deps} MISSING_DEPENDENCIES_VAR _missing_deps ) if(NOT _missing_deps) set(lut_url http://mitk.org/download/data/FibertrackingLUT.tar.gz) set(lut_tarball ${CMAKE_CURRENT_BINARY_DIR}/FibertrackingLUT.tar.gz) - #message("Downloading FiberTracking LUT ${lut_url}...") file(DOWNLOAD ${lut_url} ${lut_tarball} EXPECTED_MD5 38ecb6d4a826c9ebb0f4965eb9aeee44 TIMEOUT 60 STATUS status SHOW_PROGRESS ) list(GET status 0 status_code) list(GET status 1 status_msg) if(NOT status_code EQUAL 0) message(SEND_ERROR "${status_msg} (error code ${status_code})") endif() file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Resources) - #message("Unpacking FiberTracking LUT tarball...") execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf ../FibertrackingLUT.tar.gz WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/Resources RESULT_VARIABLE result ERROR_VARIABLE err_msg) if(result) message(SEND_ERROR "Unpacking FibertrackingLUT.tar.gz failed: ${err_msg}") endif() endif() MITK_CREATE_MODULE( SUBPROJECTS MITK-DTI INCLUDE_DIRS Fiberfox Fiberfox/SignalModels Fiberfox/Sequences Algorithms Algorithms/TrackingHandlers Algorithms/ClusteringMetrics Algorithms/GibbsTracking Algorithms/StochasticTracking IODataStructures IODataStructures/FiberBundle IODataStructures/PlanarFigureComposite Rendering ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${_module_deps} PACKAGE_DEPENDS PUBLIC ITK|ITKFFT ITK|ITKDiffusionTensorImage Vigra HDF5 ) if(MODULE_IS_ENABLED) add_subdirectory(cmdapps/Fiberfox) add_subdirectory(cmdapps/FiberProcessing) add_subdirectory(cmdapps/Tractography) add_subdirectory(cmdapps/TractographyEvaluation) add_subdirectory(cmdapps/Misc) add_subdirectory(Testing) endif() diff --git a/Modules/DiffusionImaging/FiberTracking/cmdapps/TractographyEvaluation/ExtractSimilarTracts.cpp b/Modules/DiffusionImaging/FiberTracking/cmdapps/TractographyEvaluation/ExtractSimilarTracts.cpp index a312dd42ac..c3fa02149e 100644 --- a/Modules/DiffusionImaging/FiberTracking/cmdapps/TractographyEvaluation/ExtractSimilarTracts.cpp +++ b/Modules/DiffusionImaging/FiberTracking/cmdapps/TractographyEvaluation/ExtractSimilarTracts.cpp @@ -1,238 +1,236 @@ ïğż/*=================================================================== 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 typedef itksys::SystemTools ist; typedef itk::Image ItkFloatImgType; 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()); } ItkFloatImgType::Pointer LoadItkImage(const std::string& filename) { mitk::Image::Pointer img = dynamic_cast(mitk::IOUtil::Load(filename)[0].GetPointer()); ItkFloatImgType::Pointer itkMask = ItkFloatImgType::New(); mitk::CastToItkImage(img, itkMask); return itkMask; } /*! \brief Spatially cluster fibers */ int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Extract Similar Tracts"); parser.setCategory("Fiber Tracking Evaluation"); parser.setContributor("MIC"); parser.setArgumentPrefix("--", "-"); parser.addArgument("", "i", mitkCommandLineParser::InputFile, "Input:", "input fiber bundle (.fib, .trk, .tck)", us::Any(), false); parser.addArgument("ref_tracts", "", mitkCommandLineParser::StringList, "Ref. Tracts:", "reference tracts (.fib, .trk, .tck)", us::Any(), false); parser.addArgument("ref_masks", "", mitkCommandLineParser::StringList, "Ref. Masks:", "reference bundle masks", us::Any()); parser.addArgument("", "o", mitkCommandLineParser::OutputDirectory, "Output:", "output root", us::Any(), false); parser.addArgument("distance", "", mitkCommandLineParser::Int, "Distance:", "", 10); parser.addArgument("metric", "", mitkCommandLineParser::String, "Metric:", ""); parser.addArgument("subsample", "", mitkCommandLineParser::Float, "Subsampling factor:", "Only use specified fraction of input fibers", 1.0); std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size()==0) return EXIT_FAILURE; std::string in_fib = us::any_cast(parsedArgs["i"]); std::string out_root = us::any_cast(parsedArgs["o"]); mitkCommandLineParser::StringContainerType ref_bundle_files = us::any_cast(parsedArgs["ref_tracts"]); mitkCommandLineParser::StringContainerType ref_mask_files; if (parsedArgs.count("ref_masks")) ref_mask_files = us::any_cast(parsedArgs["ref_masks"]); if (ref_mask_files.size()>0 && ref_mask_files.size()!=ref_bundle_files.size()) { MITK_INFO << "If reference masks are used, there has to be one mask per reference tract."; return EXIT_FAILURE; } int distance = 10; if (parsedArgs.count("distance")) distance = us::any_cast(parsedArgs["distance"]); std::string metric = "EU_MEAN"; if (parsedArgs.count("metric")) metric = us::any_cast(parsedArgs["metric"]); float subsample = 1.0; if (parsedArgs.count("subsample")) subsample = us::any_cast(parsedArgs["subsample"]); try { mitk::FiberBundle::Pointer fib = LoadFib(in_fib); std::srand(0); if (subsample<1.0) fib = fib->SubsampleFibers(subsample); mitk::FiberBundle::Pointer resampled_fib = fib->GetDeepCopy(); resampled_fib->ResampleToNumPoints(12); std::vector< mitk::FiberBundle::Pointer > ref_fibs; std::vector< ItkFloatImgType::Pointer > ref_masks; for (std::size_t i=0; i distances; distances.push_back(distance); mitk::FiberBundle::Pointer anchor_tractogram = mitk::FiberBundle::New(nullptr); unsigned int c = 0; for (auto ref_fib : ref_fibs) { MITK_INFO << "Extracting " << ist::GetFilenameName(ref_bundle_files.at(c)); -// std::streambuf *old = cout.rdbuf(); // <-- save -// std::stringstream ss; -// std::cout.rdbuf (ss.rdbuf()); // <-- redirect + std::streambuf *old = cout.rdbuf(); // <-- save + std::stringstream ss; + std::cout.rdbuf (ss.rdbuf()); // <-- redirect try { itk::TractClusteringFilter::Pointer segmenter = itk::TractClusteringFilter::New(); // calculate centroids from reference bundle { - MITK_INFO << "TEST 1"; itk::TractClusteringFilter::Pointer clusterer = itk::TractClusteringFilter::New(); clusterer->SetDistances({10,20,30}); clusterer->SetTractogram(ref_fib); clusterer->SetMetrics({new mitk::ClusteringMetricEuclideanStd()}); clusterer->SetMergeDuplicateThreshold(0.0); clusterer->Update(); - MITK_INFO << "TEST 2"; std::vector tracts = clusterer->GetOutCentroids(); ref_fib = mitk::FiberBundle::New(nullptr); ref_fib = ref_fib->AddBundles(tracts); mitk::IOUtil::Save(ref_fib, out_root + "centroids_" + ist::GetFilenameName(ref_bundle_files.at(c))); segmenter->SetInCentroids(ref_fib); } // segment tract segmenter->SetFilterMask(ref_masks.at(c)); segmenter->SetOverlapThreshold(0.8); segmenter->SetDistances(distances); segmenter->SetTractogram(resampled_fib); segmenter->SetMergeDuplicateThreshold(0.0); segmenter->SetDoResampling(false); if (metric=="EU_MEAN") segmenter->SetMetrics({new mitk::ClusteringMetricEuclideanMean()}); else if (metric=="EU_STD") segmenter->SetMetrics({new mitk::ClusteringMetricEuclideanStd()}); else if (metric=="EU_MAX") segmenter->SetMetrics({new mitk::ClusteringMetricEuclideanMax()}); segmenter->Update(); std::vector< std::vector< long > > clusters = segmenter->GetOutFiberIndices(); if (clusters.size()>0) { vtkSmartPointer weights = vtkSmartPointer::New(); mitk::FiberBundle::Pointer result = mitk::FiberBundle::New(nullptr); std::vector< mitk::FiberBundle::Pointer > result_fibs; for (unsigned int cluster_index=0; cluster_indexGeneratePolyDataByIds(clusters.at(cluster_index), weights))); result = result->AddBundles(result_fibs); anchor_tractogram = anchor_tractogram->AddBundle(result); mitk::IOUtil::Save(result, out_root + "anchor_" + ist::GetFilenameName(ref_bundle_files.at(c))); fib = mitk::FiberBundle::New(fib->GeneratePolyDataByIds(clusters.back(), weights)); resampled_fib = mitk::FiberBundle::New(resampled_fib->GeneratePolyDataByIds(clusters.back(), weights)); } } catch(itk::ExceptionObject& excpt) { MITK_INFO << "Exception while processing " << ist::GetFilenameName(ref_bundle_files.at(c)); MITK_INFO << excpt.GetDescription(); } catch(std::exception& excpt) { MITK_INFO << "Exception while processing " << ist::GetFilenameName(ref_bundle_files.at(c)); MITK_INFO << excpt.what(); } -// std::cout.rdbuf (old); // <-- restore + std::cout.rdbuf (old); // <-- restore if (fib->GetNumFibers()==0) break; ++c; } MITK_INFO << "Streamlines in anchor tractogram: " << anchor_tractogram->GetNumFibers(); mitk::IOUtil::Save(anchor_tractogram, out_root + "anchor_tractogram.trk"); MITK_INFO << "Streamlines remaining in candidate tractogram: " << fib->GetNumFibers(); mitk::IOUtil::Save(fib, out_root + "candidate_tractogram.trk"); } 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/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.h b/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.h index b3b99841c6..ec06faaf9b 100644 --- a/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.h +++ b/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.h @@ -1,350 +1,351 @@ /*=================================================================== 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 __mitkExtendedLabelStatisticsImageFilter #define __mitkExtendedLabelStatisticsImageFilter #include "itkLabelStatisticsImageFilter.h" namespace itk { /** * \class ExtendedLabelStatisticsImageFilter * \brief Extension of the itkLabelStatisticsImageFilter that also calculates the Skewness,Kurtosis,Entropy,Uniformity. * * This class inherits from the itkLabelStatisticsImageFilter and * uses its results for the calculation of seven additional coefficients: * the Skewness, Kurtosis, Uniformity, UPP, MPP, Entropy and Median * * */ template< class TInputImage, class TLabelImage > class ExtendedLabelStatisticsImageFilter : public LabelStatisticsImageFilter< TInputImage, TLabelImage > { public: typedef ExtendedLabelStatisticsImageFilter Self; typedef LabelStatisticsImageFilter < TInputImage, TLabelImage > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef typename Superclass::LabelPixelType LabelPixelType; typedef typename Superclass::RealType RealType; typedef typename Superclass::PixelType PixelType; typedef typename Superclass::MapIterator MapIterator; typedef typename Superclass::BoundingBoxType BoundingBoxType; typedef typename Superclass::RegionType RegionType; typedef itk::Statistics::Histogram HistogramType; itkFactorylessNewMacro( Self ); itkCloneMacro( Self ); itkTypeMacro(ExtendedLabelStatisticsImageFilter, LabelStatisticsImageFilter); /** \class LabelStatistics * \brief Statistics stored per label * \ingroup ITKImageStatistics */ class LabelStatistics { public: // default constructor LabelStatistics() { // initialized to the default values m_Count = NumericTraits< IdentifierType >::ZeroValue(); m_PositivePixelCount = NumericTraits< IdentifierType >::ZeroValue(); m_Sum = NumericTraits< RealType >::ZeroValue(); m_SumOfPositivePixels = NumericTraits< RealType >::ZeroValue(); m_SumOfSquares = NumericTraits< RealType >::ZeroValue(); m_SumOfCubes = NumericTraits< RealType >::ZeroValue(); m_SumOfQuadruples = NumericTraits< RealType >::ZeroValue(); // Set such that the first pixel encountered can be compared m_Minimum = NumericTraits< RealType >::max(); m_Maximum = NumericTraits< RealType >::NonpositiveMin(); // Default these to zero m_Mean = NumericTraits< RealType >::ZeroValue(); m_Sigma = NumericTraits< RealType >::ZeroValue(); m_Variance = NumericTraits< RealType >::ZeroValue(); m_MPP = NumericTraits< RealType >::ZeroValue(); m_Median = NumericTraits< RealType >::ZeroValue(); m_Uniformity = NumericTraits< RealType >::ZeroValue(); m_UPP = NumericTraits< RealType >::ZeroValue(); m_Entropy = NumericTraits< RealType >::ZeroValue(); m_Skewness = NumericTraits< RealType >::ZeroValue(); m_Kurtosis = NumericTraits< RealType >::ZeroValue(); unsigned int imageDimension = itkGetStaticConstMacro(ImageDimension); m_BoundingBox.resize(imageDimension * 2); for ( unsigned int i = 0; i < imageDimension * 2; i += 2 ) { m_BoundingBox[i] = NumericTraits< IndexValueType >::max(); m_BoundingBox[i + 1] = NumericTraits< IndexValueType >::NonpositiveMin(); } m_Histogram = nullptr; } // constructor with histogram enabled LabelStatistics(int size, RealType lowerBound, RealType upperBound) { // initialized to the default values m_Count = NumericTraits< IdentifierType >::ZeroValue(); m_PositivePixelCount = NumericTraits< IdentifierType >::ZeroValue(); m_Sum = NumericTraits< RealType >::ZeroValue(); m_SumOfPositivePixels = NumericTraits< RealType >::ZeroValue(); m_SumOfSquares = NumericTraits< RealType >::ZeroValue(); m_SumOfCubes = NumericTraits< RealType >::ZeroValue(); m_SumOfQuadruples = NumericTraits< RealType >::ZeroValue(); // Set such that the first pixel encountered can be compared m_Minimum = NumericTraits< RealType >::max(); m_Maximum = NumericTraits< RealType >::NonpositiveMin(); // Default these to zero m_Mean = NumericTraits< RealType >::ZeroValue(); m_Sigma = NumericTraits< RealType >::ZeroValue(); m_Variance = NumericTraits< RealType >::ZeroValue(); m_MPP = NumericTraits< RealType >::ZeroValue(); m_Median = NumericTraits< RealType >::ZeroValue(); m_Uniformity = NumericTraits< RealType >::ZeroValue(); m_UPP = NumericTraits< RealType >::ZeroValue(); m_Entropy = NumericTraits< RealType >::ZeroValue(); m_Skewness = NumericTraits< RealType >::ZeroValue(); m_Kurtosis = NumericTraits< RealType >::ZeroValue(); unsigned int imageDimension = itkGetStaticConstMacro(ImageDimension); m_BoundingBox.resize(imageDimension * 2); for ( unsigned int i = 0; i < imageDimension * 2; i += 2 ) { m_BoundingBox[i] = NumericTraits< IndexValueType >::max(); m_BoundingBox[i + 1] = NumericTraits< IndexValueType >::NonpositiveMin(); } // Histogram m_Histogram = HistogramType::New(); typename HistogramType::SizeType hsize; typename HistogramType::MeasurementVectorType lb; typename HistogramType::MeasurementVectorType ub; hsize.SetSize(1); lb.SetSize(1); ub.SetSize(1); m_Histogram->SetMeasurementVectorSize(1); hsize[0] = size; lb[0] = lowerBound; ub[0] = upperBound; m_Histogram->Initialize(hsize, lb, ub); } // need copy constructor because of smart pointer to histogram LabelStatistics(const LabelStatistics & l) { m_Count = l.m_Count; m_Minimum = l.m_Minimum; m_Maximum = l.m_Maximum; m_Mean = l.m_Mean; m_Sum = l.m_Sum; m_SumOfSquares = l.m_SumOfSquares; m_Sigma = l.m_Sigma; m_Variance = l.m_Variance; m_MPP = l.m_MPP; m_Median = l.m_Median; m_Uniformity = l.m_Uniformity; m_UPP = l.m_UPP; m_Entropy = l.m_Entropy; m_Skewness = l.m_Skewness; m_Kurtosis = l.m_Kurtosis; m_BoundingBox = l.m_BoundingBox; m_Histogram = l.m_Histogram; m_SumOfPositivePixels = l.m_SumOfPositivePixels; m_PositivePixelCount = l.m_PositivePixelCount; m_SumOfCubes = l.m_SumOfCubes; m_SumOfQuadruples = l.m_SumOfQuadruples; } // added for completeness LabelStatistics &operator= (const LabelStatistics& l) { if(this != &l) { m_Count = l.m_Count; m_Minimum = l.m_Minimum; m_Maximum = l.m_Maximum; m_Mean = l.m_Mean; m_Sum = l.m_Sum; m_SumOfSquares = l.m_SumOfSquares; m_Sigma = l.m_Sigma; m_Variance = l.m_Variance; m_MPP = l.m_MPP; m_Median = l.m_Median; m_Uniformity = l.m_Uniformity; m_UPP = l.m_UPP; m_Entropy = l.m_Entropy; m_Skewness = l.m_Skewness; m_Kurtosis = l.m_Kurtosis; m_BoundingBox = l.m_BoundingBox; m_Histogram = l.m_Histogram; m_SumOfPositivePixels = l.m_SumOfPositivePixels; m_PositivePixelCount = l.m_PositivePixelCount; m_SumOfCubes = l.m_SumOfCubes; m_SumOfQuadruples = l.m_SumOfQuadruples; } return *this; } IdentifierType m_Count; RealType m_Minimum; RealType m_Maximum; RealType m_Mean; RealType m_Sum; RealType m_SumOfSquares; RealType m_Sigma; RealType m_Variance; RealType m_MPP; RealType m_Median; RealType m_Uniformity; RealType m_UPP; RealType m_Entropy; RealType m_Skewness; RealType m_Kurtosis; IdentifierType m_PositivePixelCount; RealType m_SumOfPositivePixels; RealType m_SumOfCubes; RealType m_SumOfQuadruples; typename Superclass::BoundingBoxType m_BoundingBox; typename HistogramType::Pointer m_Histogram; }; /** Type of the map used to store data per label */ typedef itksys::hash_map< LabelPixelType, LabelStatistics > MapType; typedef typename itksys::hash_map< LabelPixelType, LabelStatistics >::const_iterator StatisticsMapConstIterator; typedef typename itksys::hash_map< LabelPixelType, LabelStatistics >::iterator StatisticsMapIterator; typedef IdentifierType MapSizeType; /** Type of the container used to store valid label values */ typedef std::vector ValidLabelValuesContainerType; /** Return the computed Minimum for a label. */ RealType GetMinimum(LabelPixelType label) const; /** Return the computed Maximum for a label. */ RealType GetMaximum(LabelPixelType label) const; /** Return the computed Mean for a label. */ RealType GetMean(LabelPixelType label) const; /** Return the computed Standard Deviation for a label. */ RealType GetSigma(LabelPixelType label) const; /** Return the computed Variance for a label. */ RealType GetVariance(LabelPixelType label) const; /** Return the computed bounding box for a label. */ BoundingBoxType GetBoundingBox(LabelPixelType label) const; /** Return the computed region. */ RegionType GetRegion(LabelPixelType label) const; /** Return the compute Sum for a label. */ RealType GetSum(LabelPixelType label) const; /** Return the number of pixels for a label. */ MapSizeType GetCount(LabelPixelType label) const; /** Return the histogram for a label */ HistogramType::Pointer GetHistogram(LabelPixelType label) const; /*getter method for the new statistics*/ RealType GetSkewness(LabelPixelType label) const; RealType GetKurtosis(LabelPixelType label) const; RealType GetUniformity( LabelPixelType label) const; RealType GetMedian( LabelPixelType label) const; RealType GetEntropy( LabelPixelType label) const; RealType GetMPP( LabelPixelType label) const; RealType GetUPP( LabelPixelType label) const; bool GetMaskingNonEmpty() const; std::list GetRelevantLabels() const; /** specify global Histogram parameters. If the histogram parameters are set with this function, the same min and max value are used for all histograms. */ void SetHistogramParameters(const int numBins, RealType lowerBound, RealType upperBound); /** specify Histogram parameters for each label individually. Labels in the label image that are not represented in the std::maps here will receive global parameters (if available) */ void SetHistogramParametersForLabels(std::map numBins, std::map lowerBound, std::map upperBound); protected: ExtendedLabelStatisticsImageFilter(): m_GlobalHistogramParametersSet(false), + m_MaskNonEmpty(false), m_LabelHistogramParametersSet(false), m_PreferGlobalHistogramParameters(false) { m_NumBins.set_size(1); } ~ExtendedLabelStatisticsImageFilter() override{} void AfterThreadedGenerateData() override; /** Initialize some accumulators before the threads run. */ void BeforeThreadedGenerateData() override; /** Multi-thread version GenerateData. */ void ThreadedGenerateData(const typename TInputImage::RegionType & outputRegionForThread, ThreadIdType threadId) override; /** Does the specified label exist? Can only be called after a call * a call to Update(). */ bool HasLabel(LabelPixelType label) const { return m_LabelStatistics.find(label) != m_LabelStatistics.end(); } private: std::vector< MapType > m_LabelStatisticsPerThread; MapType m_LabelStatistics; ValidLabelValuesContainerType m_ValidLabelValues; bool m_GlobalHistogramParametersSet; typename HistogramType::SizeType m_NumBins; RealType m_LowerBound; RealType m_UpperBound; bool m_MaskNonEmpty; bool m_LabelHistogramParametersSet; std::map m_LabelMin, m_LabelMax; std::map m_LabelNBins; bool m_PreferGlobalHistogramParameters; }; // end of class } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "mitkExtendedLabelStatisticsImageFilter.hxx" #endif #endif diff --git a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx index 9ea49c069b..bb8e3b7e3f 100644 --- a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx +++ b/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx @@ -1,477 +1,474 @@ /*=================================================================== 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 __mitkExtendedStatisticsImageFilter_hxx #define __mitkExtendedStatisticsImageFilter_hxx #include "mitkExtendedStatisticsImageFilter.h" #include namespace itk { template< class TInputImage > ExtendedStatisticsImageFilter< TInputImage >::ExtendedStatisticsImageFilter() : StatisticsImageFilter< TInputImage >(), m_ThreadSum(1), m_SumOfSquares(1), m_SumOfCubes(1), m_SumOfQuadruples(1), m_Count(1), m_PositivePixelCount(1), m_ThreadSumOfPositivePixels(1), m_ThreadMin(1), m_ThreadMax(1), m_UseHistogram(false), m_HistogramCalculated(false) { /* * add the Skewness,Kurtosis,Entropy,Uniformity,MPP, UPP, Median to the other statistical calculated Values * of the mitkStatisticsImageFilter as the 7th to the 13th Output */ for ( int i = 7; i < 14; ++i ) { typename RealObjectType::Pointer output = static_cast< RealObjectType * >( this->MakeOutput(i).GetPointer() ); this->ProcessObject::SetNthOutput( i, output.GetPointer() ); } this->GetSkewnessOutput()->Set( 0.0 ); this->GetKurtosisOutput()->Set( 0.0 ); this->GetEntropyOutput()->Set( -1.0 ); this->GetUniformityOutput()->Set( 0.0 ); this->GetUPPOutput()->Set( 0.0 ); this->GetMPPOutput()->Set( 0.0 ); this->GetMedianOutput()->Set( 0.0 ); m_Histogram = ITK_NULLPTR; m_HistogramPerThread.resize(0); } template< class TInputImage > DataObject::Pointer ExtendedStatisticsImageFilter< TInputImage >::MakeOutput( ProcessObject::DataObjectPointerArraySizeType output) { switch ( output ) { case 7: case 8: case 9: case 10: case 11: case 12: { return RealObjectType::New().GetPointer(); - break; } case 13: { return RealObjectType::New().GetPointer(); - break; } default: { // might as well make an image return Superclass::MakeOutput( output ); - break; } } } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetSkewnessOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(7) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetSkewnessOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(7) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetKurtosisOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(8) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetKurtosisOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(8) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetUniformityOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(9) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetUniformityOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(9) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetEntropyOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(10) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetEntropyOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(10) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetUPPOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(11) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetUPPOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(11) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetMPPOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(12) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetMPPOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(12) ); } template< class TInputImage > typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetMedianOutput() { return static_cast< RealObjectType * >( this->ProcessObject::GetOutput(13) ); } template< class TInputImage > const typename ExtendedStatisticsImageFilter< TInputImage >::RealObjectType * ExtendedStatisticsImageFilter< TInputImage > ::GetMedianOutput() const { return static_cast< const RealObjectType * >( this->ProcessObject::GetOutput(13) ); } template< typename TInputImage > void ExtendedStatisticsImageFilter< TInputImage > ::BeforeThreadedGenerateData() { ThreadIdType numberOfThreads = this->GetNumberOfThreads(); if (m_UseHistogram) { // Histogram m_Histogram = HistogramType::New(); typename HistogramType::SizeType hsize; typename HistogramType::MeasurementVectorType lb; typename HistogramType::MeasurementVectorType ub; hsize.SetSize(1); lb.SetSize(1); ub.SetSize(1); m_Histogram->SetMeasurementVectorSize(1); hsize[0] = m_NumBins; lb[0] = m_LowerBound; ub[0] = m_UpperBound; m_Histogram->Initialize(hsize, lb, ub); m_HistogramPerThread.resize(numberOfThreads); for (unsigned int i = 0; i < numberOfThreads; i++) { typename HistogramType::Pointer hist = HistogramType::New(); typename HistogramType::SizeType hsize; typename HistogramType::MeasurementVectorType lb; typename HistogramType::MeasurementVectorType ub; hsize.SetSize(1); lb.SetSize(1); ub.SetSize(1); hist->SetMeasurementVectorSize(1); hsize[0] = m_NumBins; lb[0] = m_LowerBound; ub[0] = m_UpperBound; hist->Initialize(hsize, lb, ub); m_HistogramPerThread[i] = hist; } } // Resize the thread temporaries m_Count.SetSize(numberOfThreads); m_SumOfSquares.SetSize(numberOfThreads); m_SumOfCubes.SetSize(numberOfThreads); m_SumOfQuadruples.SetSize(numberOfThreads); m_ThreadSum.SetSize(numberOfThreads); m_ThreadMin.SetSize(numberOfThreads); m_ThreadMax.SetSize(numberOfThreads); m_PositivePixelCount.SetSize(numberOfThreads); m_ThreadSumOfPositivePixels.SetSize(numberOfThreads); // Initialize the temporaries m_Count.Fill(NumericTraits< SizeValueType >::Zero); m_ThreadSum.Fill(NumericTraits< RealType >::Zero); m_SumOfSquares.Fill(NumericTraits< RealType >::Zero); m_SumOfCubes.Fill(NumericTraits< RealType >::Zero); m_SumOfQuadruples.Fill(NumericTraits< RealType >::Zero); m_ThreadMin.Fill( NumericTraits< PixelType >::max() ); m_ThreadMax.Fill( NumericTraits< PixelType >::NonpositiveMin() ); m_PositivePixelCount.Fill(NumericTraits< SizeValueType >::Zero); m_ThreadSumOfPositivePixels.Fill(NumericTraits< SizeValueType >::Zero); } template< typename TInputImage > void ExtendedStatisticsImageFilter< TInputImage > ::ThreadedGenerateData(const typename StatisticsImageFilter::RegionType & outputRegionForThread, ThreadIdType threadId) { const SizeValueType size0 = outputRegionForThread.GetSize(0); if( size0 == 0) { return; } RealType realValue; PixelType value; typename HistogramType::IndexType histogramIndex(1); typename HistogramType::MeasurementVectorType histogramMeasurement(1); RealType sum = NumericTraits< RealType >::ZeroValue(); RealType sumOfPositivePixels = NumericTraits< RealType >::ZeroValue(); RealType sumOfSquares = NumericTraits< RealType >::ZeroValue(); RealType sumOfCubes = NumericTraits< RealType >::ZeroValue(); RealType sumOfQuadruples = NumericTraits< RealType >::ZeroValue(); SizeValueType count = NumericTraits< SizeValueType >::ZeroValue(); SizeValueType countOfPositivePixels = NumericTraits< SizeValueType >::ZeroValue(); PixelType min = NumericTraits< PixelType >::max(); PixelType max = NumericTraits< PixelType >::NonpositiveMin(); ImageScanlineConstIterator< TInputImage > it (this->GetInput(), outputRegionForThread); // support progress methods/callbacks const size_t numberOfLinesToProcess = outputRegionForThread.GetNumberOfPixels() / size0; ProgressReporter progress( this, threadId, numberOfLinesToProcess ); // do the work while ( !it.IsAtEnd() ) { while ( !it.IsAtEndOfLine() ) { value = it.Get(); realValue = static_cast< RealType >( value ); if (m_UseHistogram) { histogramMeasurement[0] = value; m_HistogramPerThread[threadId]->GetIndex(histogramMeasurement, histogramIndex); m_HistogramPerThread[threadId]->IncreaseFrequencyOfIndex(histogramIndex, 1); } if ( value < min ) { min = value; } if ( value > max ) { max = value; } if (value > 0) { sumOfPositivePixels += realValue; ++countOfPositivePixels; } sum += realValue; sumOfSquares += ( realValue * realValue ); sumOfCubes += std::pow(realValue, 3.); sumOfQuadruples += std::pow(realValue, 4.); ++count; ++it; } it.NextLine(); progress.CompletedPixel(); } m_ThreadSum[threadId] = sum; m_SumOfSquares[threadId] = sumOfSquares; m_Count[threadId] = count; m_ThreadMin[threadId] = min; m_ThreadMax[threadId] = max; m_ThreadSumOfPositivePixels[threadId] = sumOfPositivePixels; m_PositivePixelCount[threadId] = countOfPositivePixels; m_SumOfCubes[threadId] = sumOfCubes; m_SumOfQuadruples[threadId] = sumOfQuadruples; } template< class TInputImage > void ExtendedStatisticsImageFilter< TInputImage > ::AfterThreadedGenerateData() { ThreadIdType i; SizeValueType count = 0; SizeValueType countOfPositivePixels = 0; RealType sumOfSquares = 0; RealType sumOfCubes = 0; RealType sumOfQuadruples = 0; RealType sumOfPositivePixels = 0; ThreadIdType numberOfThreads = this->GetNumberOfThreads(); PixelType minimum; PixelType maximum; RealType mean; RealType meanOfPositivePixels; RealType sigma; RealType variance; RealType skewness; RealType kurtosis; RealType sum; sum = sumOfSquares = sumOfCubes = sumOfQuadruples = NumericTraits< RealType >::ZeroValue(); count = countOfPositivePixels = 0; // Find the min/max over all threads and accumulate count, sum and // sum of squares minimum = NumericTraits< PixelType >::max(); maximum = NumericTraits< PixelType >::NonpositiveMin(); for ( i = 0; i < numberOfThreads; i++ ) { count += m_Count[i]; sum += m_ThreadSum[i]; sumOfSquares += m_SumOfSquares[i]; sumOfCubes += m_SumOfCubes[i]; sumOfQuadruples += m_SumOfQuadruples[i]; sumOfPositivePixels += m_ThreadSumOfPositivePixels[i]; countOfPositivePixels += m_PositivePixelCount[i]; if ( m_ThreadMin[i] < minimum ) { minimum = m_ThreadMin[i]; } if ( m_ThreadMax[i] > maximum ) { maximum = m_ThreadMax[i]; } // if enabled, update the histogram for this label if ( m_UseHistogram ) { typename HistogramType::IndexType index; index.SetSize(1); for ( int bin = 0; bin < m_NumBins; ++bin ) { index[0] = bin; m_Histogram->IncreaseFrequency( bin, m_HistogramPerThread[i]->GetFrequency(bin) ); } } } // compute statistics mean = sum / static_cast< RealType >( count ); meanOfPositivePixels = sumOfPositivePixels / static_cast< RealType >( countOfPositivePixels ); // unbiased estimate variance = ( sumOfSquares - ( sum * sum / static_cast< RealType >( count ) ) ) / ( static_cast< RealType >( count ) ); RealType secondMoment, thirdMoment, fourthMoment; secondMoment = sumOfSquares / static_cast< RealType >( count ); thirdMoment = sumOfCubes / static_cast< RealType >( count ); fourthMoment = sumOfQuadruples / static_cast< RealType >( count ); skewness = (thirdMoment - 3. * secondMoment * mean + 2. * std::pow(mean, 3.)) / std::pow(secondMoment - std::pow(mean, 2.), 1.5); // see http://www.boost.org/doc/libs/1_51_0/doc/html/boost/accumulators/impl/skewness_impl.html kurtosis = (fourthMoment - 4. * thirdMoment * mean + 6. * secondMoment * std::pow(mean, 2.) - 3. * std::pow(mean, 4.)) / std::pow(secondMoment - std::pow(mean, 2.), 2.); // see http://www.boost.org/doc/libs/1_46_1/doc/html/boost/accumulators/impl/kurtosis_impl.html, dropped -3 sigma = std::sqrt(variance); // Set the outputs this->GetMinimumOutput()->Set(minimum); this->GetMaximumOutput()->Set(maximum); this->GetMeanOutput()->Set(mean); this->GetSigmaOutput()->Set(sigma); this->GetVarianceOutput()->Set(variance); this->GetSumOutput()->Set(sum); this->GetKurtosisOutput()->Set(kurtosis); this->GetSkewnessOutput()->Set(skewness); this->GetMPPOutput()->Set(meanOfPositivePixels); if (m_UseHistogram) { mitk::HistogramStatisticsCalculator histStatCalc; histStatCalc.SetHistogram(m_Histogram); histStatCalc.CalculateStatistics(); this->GetUniformityOutput()->Set(histStatCalc.GetUniformity()); this->GetUPPOutput()->Set(histStatCalc.GetUPP()); this->GetEntropyOutput()->Set(histStatCalc.GetEntropy()); this->GetMedianOutput()->Set(histStatCalc.GetMedian()); } m_HistogramCalculated = true; } template< typename TInputImage > void ExtendedStatisticsImageFilter< TInputImage > ::SetHistogramParameters(const int numBins, RealType lowerBound, RealType upperBound) { m_NumBins = numBins; m_LowerBound = lowerBound; m_UpperBound = upperBound; m_UseHistogram = true; } } #endif diff --git a/Modules/ImageStatistics/mitkHistogramStatisticsCalculator.cpp b/Modules/ImageStatistics/mitkHistogramStatisticsCalculator.cpp index b7af6a479d..be86834261 100644 --- a/Modules/ImageStatistics/mitkHistogramStatisticsCalculator.cpp +++ b/Modules/ImageStatistics/mitkHistogramStatisticsCalculator.cpp @@ -1,108 +1,107 @@ #include #include namespace mitk { HistogramStatisticsCalculator::HistogramStatisticsCalculator(): - m_StatisticsCalculated(false) + m_Uniformity(0), m_UPP(0), m_Entropy(0), m_Median(0), m_StatisticsCalculated(false) { } void HistogramStatisticsCalculator::SetHistogram(HistogramType::Pointer histogram) { if (m_Histogram != histogram) { m_Histogram = histogram; m_StatisticsCalculated = false; } } HistogramStatisticsCalculator::MeasurementType HistogramStatisticsCalculator::GetEntropy() { if (!m_StatisticsCalculated) { MITK_WARN("Statistics have not yet been calculated, running calculation now..."); CalculateStatistics(); } return m_Entropy; } HistogramStatisticsCalculator::MeasurementType HistogramStatisticsCalculator::GetMedian() { if (!m_StatisticsCalculated) { MITK_WARN("Statistics have not yet been calculated, running calculation now..."); CalculateStatistics(); } return m_Median; } HistogramStatisticsCalculator::MeasurementType HistogramStatisticsCalculator::GetUniformity() { if (!m_StatisticsCalculated) { MITK_WARN("Statistics have not yet been calculated, running calculation now..."); CalculateStatistics(); } return m_Uniformity; } HistogramStatisticsCalculator::MeasurementType HistogramStatisticsCalculator::GetUPP() { if (!m_StatisticsCalculated) { MITK_WARN("Statistics have not yet been calculated, running calculation now..."); CalculateStatistics(); } return m_UPP; } void HistogramStatisticsCalculator::CalculateStatistics() { if (m_Histogram.IsNull()) { throw std::runtime_error("Histogram not set in HistogramStatisticsCalculator::CalculateStatistics()"); } unsigned int nBins = m_Histogram->GetSize()[0]; m_Uniformity = 0; m_Entropy = 0; m_UPP = 0; m_Median = 0; MeasurementType cumulativeProbability = 0.0; - MeasurementType partialProbability; bool medianFound(false); for (unsigned int i = 0; i < nBins; i++) { - partialProbability = m_Histogram->GetFrequency(i, 0) / double( m_Histogram->GetTotalFrequency() ); + MeasurementType partialProbability = m_Histogram->GetFrequency(i, 0) / double( m_Histogram->GetTotalFrequency() ); cumulativeProbability += partialProbability; if (partialProbability != 0) { m_Entropy -= partialProbability * (std::log2( partialProbability )); m_Uniformity += std::pow(partialProbability, 2); if (m_Histogram->GetMeasurement(i, 0) > 0) { m_UPP += std::pow(partialProbability, 2); } } if (cumulativeProbability >= 0.5 && !medianFound) { MeasurementType binMin = m_Histogram->GetBinMin(0, i); MeasurementType binMax = m_Histogram->GetBinMax(0, i); m_Median = (binMax + binMin) / 2.0; medianFound = true; } } m_StatisticsCalculated = true; } } diff --git a/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp index 5f8a2f6844..9e594ba377 100644 --- a/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp +++ b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp @@ -1,602 +1,601 @@ #include #include #include #include #include #include "mitkImageAccessByItk.h" #include #include #include namespace mitk { HotspotMaskGenerator::HotspotMaskGenerator(): m_HotspotRadiusinMM(6.2035049089940), // radius of a 1cm3 sphere in mm m_HotspotMustBeCompletelyInsideImage(true), m_Label(1) { m_TimeStep = 0; m_InternalMask = mitk::Image::New(); m_InternalMaskUpdateTime = 0; } void HotspotMaskGenerator::SetInputImage(mitk::Image::Pointer inputImage) { if (inputImage != m_inputImage) { m_inputImage = inputImage; m_ConvolutionImageMaxIndex.set_size(inputImage->GetDimension()); m_ConvolutionImageMinIndex.set_size(inputImage->GetDimension()); this->Modified(); } } void HotspotMaskGenerator::SetMask(MaskGenerator::Pointer mask) { if (mask != m_Mask) { m_Mask = mask; this->Modified(); } } HotspotMaskGenerator::~HotspotMaskGenerator() { } void HotspotMaskGenerator::SetHotspotRadiusInMM(double radiusInMillimeter) { if(radiusInMillimeter != m_HotspotRadiusinMM) { m_HotspotRadiusinMM = radiusInMillimeter; this->Modified(); } } const double& HotspotMaskGenerator::GetHotspotRadiusinMM() const { return m_HotspotRadiusinMM; } bool HotspotMaskGenerator::GetHotspotMustBeCompletelyInsideImage() const { return m_HotspotMustBeCompletelyInsideImage; } void HotspotMaskGenerator::SetHotspotMustBeCompletelyInsideImage(bool mustBeCompletelyInImage) { if (m_HotspotMustBeCompletelyInsideImage != mustBeCompletelyInImage) { m_HotspotMustBeCompletelyInsideImage = mustBeCompletelyInImage; this->Modified(); } } mitk::Image::Pointer HotspotMaskGenerator::GetMask() { if (IsUpdateRequired()) { if ( m_inputImage.IsNull() ) { throw std::runtime_error( "Error: image empty!" ); } if ( m_TimeStep >= m_inputImage->GetTimeSteps() ) { throw std::runtime_error( "Error: invalid time step!" ); } mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput( m_inputImage ); imageTimeSelector->SetTimeNr( m_TimeStep ); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer timeSliceImage = imageTimeSelector->GetOutput(); m_internalImage = timeSliceImage; m_internalMask2D = nullptr; // is this correct when this variable holds a smart pointer? m_internalMask3D = nullptr; if ( m_Mask != nullptr ) { m_Mask->SetTimeStep(m_TimeStep); mitk::Image::Pointer timeSliceMask = m_Mask->GetMask(); if ( m_internalImage->GetDimension() == 3 ) { CastToItkImage(timeSliceMask, m_internalMask3D); AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 3, m_internalMask3D, m_Label); } else if ( m_internalImage->GetDimension() == 2 ) { CastToItkImage(timeSliceMask, m_internalMask2D); AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 2, m_internalMask2D, m_Label); } else { throw std::runtime_error( "Error: invalid image dimension" ); } } else { if ( m_internalImage->GetDimension() == 3 ) { AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 3, m_internalMask3D, m_Label); } else if ( m_internalImage->GetDimension() == 2 ) { AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 2, m_internalMask2D, m_Label); } else { throw std::runtime_error( "Error: invalid image dimension" ); } } this->Modified(); } m_InternalMaskUpdateTime = m_InternalMask->GetMTime(); return m_InternalMask; } void HotspotMaskGenerator::SetTimeStep(unsigned int timeStep) { if (m_TimeStep != timeStep) { m_TimeStep = timeStep; } } void HotspotMaskGenerator::SetLabel(unsigned short label) { if (label != m_Label) { m_Label = label; this->Modified(); } } vnl_vector HotspotMaskGenerator::GetConvolutionImageMinIndex() { this->GetMask(); // make sure we are up to date return m_ConvolutionImageMinIndex; } vnl_vector HotspotMaskGenerator::GetHotspotIndex() { this->GetMask(); // make sure we are up to date return m_ConvolutionImageMaxIndex; } template HotspotMaskGenerator::ImageExtrema HotspotMaskGenerator::CalculateExtremaWorld( const itk::Image* inputImage, typename itk::Image::Pointer maskImage, double neccessaryDistanceToImageBorderInMM, unsigned int label ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; typedef itk::ImageRegionConstIteratorWithIndex MaskImageIteratorType; typedef itk::ImageRegionConstIteratorWithIndex InputImageIndexIteratorType; typename ImageType::SpacingType spacing = inputImage->GetSpacing(); ImageExtrema minMax; minMax.Defined = false; minMax.MaxIndex.set_size(VImageDimension); minMax.MaxIndex.set_size(VImageDimension); typename ImageType::RegionType allowedExtremaRegion = inputImage->GetLargestPossibleRegion(); bool keepDistanceToImageBorders( neccessaryDistanceToImageBorderInMM > 0 ); if (keepDistanceToImageBorders) { itk::IndexValueType distanceInPixels[VImageDimension]; for(unsigned short dimension = 0; dimension < VImageDimension; ++dimension) { // To confirm that the whole hotspot is inside the image we have to keep a specific distance to the image-borders, which is as long as // the radius. To get the amount of indices we divide the radius by spacing and add 0.5 because voxels are center based: // For example with a radius of 2.2 and a spacing of 1 two indices are enough because 2.2 / 1 + 0.5 = 2.7 => 2. // But with a radius of 2.7 we need 3 indices because 2.7 / 1 + 0.5 = 3.2 => 3 distanceInPixels[dimension] = int( neccessaryDistanceToImageBorderInMM / spacing[dimension] + 0.5); } allowedExtremaRegion.ShrinkByRadius(distanceInPixels); } InputImageIndexIteratorType imageIndexIt(inputImage, allowedExtremaRegion); float maxValue = itk::NumericTraits::min(); float minValue = itk::NumericTraits::max(); typename ImageType::IndexType maxIndex; typename ImageType::IndexType minIndex; for(unsigned short i = 0; i < VImageDimension; ++i) { maxIndex[i] = 0; minIndex[i] = 0; } if (maskImage != nullptr) { MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion()); typename ImageType::IndexType imageIndex; typename ImageType::PointType worldPosition; typename ImageType::IndexType maskIndex; for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt) { imageIndex = maskIndex = maskIt.GetIndex(); if(maskIt.Get() == label) { if( allowedExtremaRegion.IsInside(imageIndex) ) { imageIndexIt.SetIndex( imageIndex ); double value = imageIndexIt.Get(); minMax.Defined = true; //Calculate minimum, maximum and corresponding index-values if( value > maxValue ) { maxIndex = imageIndexIt.GetIndex(); maxValue = value; } if(value < minValue ) { minIndex = imageIndexIt.GetIndex(); minValue = value; } } } } } else { for(imageIndexIt.GoToBegin(); !imageIndexIt.IsAtEnd(); ++imageIndexIt) { double value = imageIndexIt.Get(); minMax.Defined = true; //Calculate minimum, maximum and corresponding index-values if( value > maxValue ) { maxIndex = imageIndexIt.GetIndex(); maxValue = value; } if(value < minValue ) { minIndex = imageIndexIt.GetIndex(); minValue = value; } } } minMax.MaxIndex.set_size(VImageDimension); minMax.MinIndex.set_size(VImageDimension); for(unsigned int i = 0; i < minMax.MaxIndex.size(); ++i) { minMax.MaxIndex[i] = maxIndex[i]; } for(unsigned int i = 0; i < minMax.MinIndex.size(); ++i) { minMax.MinIndex[i] = minIndex[i]; } minMax.Max = maxValue; minMax.Min = minValue; return minMax; } template itk::Size HotspotMaskGenerator::CalculateConvolutionKernelSize( double spacing[VImageDimension], double radiusInMM ) { typedef itk::Image< float, VImageDimension > KernelImageType; typedef typename KernelImageType::SizeType SizeType; SizeType maskSize; for(unsigned int i = 0; i < VImageDimension; ++i) { maskSize[i] = static_cast( 2 * radiusInMM / spacing[i]); // We always want an uneven size to have a clear center point in the convolution mask if(maskSize[i] % 2 == 0 ) { ++maskSize[i]; } } return maskSize; } template itk::SmartPointer< itk::Image > HotspotMaskGenerator::GenerateHotspotSearchConvolutionKernel(double mmPerPixel[VImageDimension], double radiusInMM ) { std::stringstream ss; for (unsigned int i = 0; i < VImageDimension; ++i) { ss << mmPerPixel[i]; if (i < VImageDimension -1) ss << ","; } MITK_DEBUG << "Update convolution kernel for spacing (" << ss.str() << ") and radius " << radiusInMM << "mm"; double radiusInMMSquared = radiusInMM * radiusInMM; typedef itk::Image< float, VImageDimension > KernelImageType; typename KernelImageType::Pointer convolutionKernel = KernelImageType::New(); // Calculate size and allocate mask image typedef typename KernelImageType::SizeType SizeType; SizeType maskSize = this->CalculateConvolutionKernelSize(mmPerPixel, radiusInMM); mitk::Point3D convolutionMaskCenterIndex; convolutionMaskCenterIndex.Fill(0.0); for(unsigned int i = 0; i < VImageDimension; ++i) { convolutionMaskCenterIndex[i] = 0.5 * (double)(maskSize[i]-1); } typedef typename KernelImageType::IndexType IndexType; IndexType maskIndex; maskIndex.Fill(0); typedef typename KernelImageType::RegionType RegionType; RegionType maskRegion; maskRegion.SetSize(maskSize); maskRegion.SetIndex(maskIndex); convolutionKernel->SetRegions(maskRegion); convolutionKernel->SetSpacing(mmPerPixel); convolutionKernel->Allocate(); // Fill mask image values by subsampling the image grid typedef itk::ImageRegionIteratorWithIndex MaskIteratorType; MaskIteratorType maskIt(convolutionKernel,maskRegion); int numberOfSubVoxelsPerDimension = 2; // per dimension! int numberOfSubVoxels = ::pow( static_cast(numberOfSubVoxelsPerDimension), static_cast(VImageDimension) ); double subVoxelSizeInPixels = 1.0 / (double)numberOfSubVoxelsPerDimension; double valueOfOneSubVoxel = 1.0 / (double)numberOfSubVoxels; - double maskValue = 0.0; mitk::Point3D subVoxelIndexPosition; double distanceSquared = 0.0; typedef itk::ContinuousIndex ContinuousIndexType; for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt) { ContinuousIndexType indexPoint(maskIt.GetIndex()); mitk::Point3D voxelPosition; for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension) { voxelPosition[dimension] = indexPoint[dimension]; } - maskValue = 0.0; + double maskValue = 0.0; mitk::Vector3D subVoxelOffset; subVoxelOffset.Fill(0.0); // iterate sub-voxels by iterating all possible offsets for (subVoxelOffset[0] = -0.5 + subVoxelSizeInPixels / 2.0; subVoxelOffset[0] < +0.5; subVoxelOffset[0] += subVoxelSizeInPixels) { for (subVoxelOffset[1] = -0.5 + subVoxelSizeInPixels / 2.0; subVoxelOffset[1] < +0.5; subVoxelOffset[1] += subVoxelSizeInPixels) { for (subVoxelOffset[2] = -0.5 + subVoxelSizeInPixels / 2.0; subVoxelOffset[2] < +0.5; subVoxelOffset[2] += subVoxelSizeInPixels) { subVoxelIndexPosition = voxelPosition + subVoxelOffset; // this COULD be integrated into the for-loops if neccessary (add voxelPosition to initializer and end condition) distanceSquared = (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0] * (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0] + (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1] * (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1] + (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2] * (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2]; if (distanceSquared <= radiusInMMSquared) { maskValue += valueOfOneSubVoxel; } } } } maskIt.Set( maskValue ); } return convolutionKernel; } template itk::SmartPointer > HotspotMaskGenerator::GenerateConvolutionImage( const itk::Image* inputImage ) { double mmPerPixel[VImageDimension]; for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension) { mmPerPixel[dimension] = inputImage->GetSpacing()[dimension]; } // update convolution kernel typedef itk::Image< float, VImageDimension > KernelImageType; typename KernelImageType::Pointer convolutionKernel = this->GenerateHotspotSearchConvolutionKernel(mmPerPixel, m_HotspotRadiusinMM); // update convolution image typedef itk::Image< TPixel, VImageDimension > InputImageType; typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType; typedef itk::FFTConvolutionImageFilter ConvolutionFilterType; typename ConvolutionFilterType::Pointer convolutionFilter = ConvolutionFilterType::New(); typedef itk::ConstantBoundaryCondition BoundaryConditionType; BoundaryConditionType boundaryCondition; boundaryCondition.SetConstant(0.0); if (m_HotspotMustBeCompletelyInsideImage) { // overwrite default boundary condition convolutionFilter->SetBoundaryCondition(&boundaryCondition); } convolutionFilter->SetInput(inputImage); convolutionFilter->SetKernelImage(convolutionKernel); convolutionFilter->SetNormalize(true); MITK_DEBUG << "Update Convolution image for hotspot search"; convolutionFilter->UpdateLargestPossibleRegion(); typename ConvolutionImageType::Pointer convolutionImage = convolutionFilter->GetOutput(); convolutionImage->SetSpacing( inputImage->GetSpacing() ); // only workaround because convolution filter seems to ignore spacing of input image return convolutionImage; } template < typename TPixel, unsigned int VImageDimension> void HotspotMaskGenerator::FillHotspotMaskPixels( itk::Image* maskImage, itk::Point sphereCenter, double sphereRadiusInMM ) { typedef itk::Image< TPixel, VImageDimension > MaskImageType; typedef itk::ImageRegionIteratorWithIndex MaskImageIteratorType; MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion()); typename MaskImageType::IndexType maskIndex; typename MaskImageType::PointType worldPosition; // this is not very smart. I would rather use a 0 initialized mask (not the case here -> blame CalculateHotspotMask) and find the region where I need to iterate over, then iterate only over the small region for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt) { maskIndex = maskIt.GetIndex(); maskImage->TransformIndexToPhysicalPoint(maskIndex, worldPosition); maskIt.Set( worldPosition.EuclideanDistanceTo(sphereCenter) <= sphereRadiusInMM ? 1 : 0 ); } } template void HotspotMaskGenerator::CalculateHotspotMask(itk::Image* inputImage, typename itk::Image::Pointer maskImage, unsigned int label) { typedef itk::Image< TPixel, VImageDimension > InputImageType; typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; typename ConvolutionImageType::Pointer convolutionImage = this->GenerateConvolutionImage(inputImage); if (convolutionImage.IsNull()) { MITK_ERROR << "Empty convolution image in CalculateHotspotStatistics(). We should never reach this state (logic error)."; throw std::logic_error("Empty convolution image in CalculateHotspotStatistics()"); } // if mask image is not defined, create an image of the same size as inputImage and fill it with 1's // there is maybe a better way to do this!? if (maskImage == nullptr) { maskImage = MaskImageType::New(); typename MaskImageType::RegionType maskRegion = inputImage->GetLargestPossibleRegion(); typename MaskImageType::SpacingType maskSpacing = inputImage->GetSpacing(); typename MaskImageType::PointType maskOrigin = inputImage->GetOrigin(); typename MaskImageType::DirectionType maskDirection = inputImage->GetDirection(); maskImage->SetRegions(maskRegion); maskImage->Allocate(); maskImage->SetOrigin(maskOrigin); maskImage->SetSpacing(maskSpacing); maskImage->SetDirection(maskDirection); maskImage->FillBuffer(1); label = 1; } // find maximum in convolution image, given the current mask double requiredDistanceToBorder = m_HotspotMustBeCompletelyInsideImage ? m_HotspotRadiusinMM : -1.0; ImageExtrema convolutionImageInformation = CalculateExtremaWorld(convolutionImage.GetPointer(), maskImage, requiredDistanceToBorder, label); bool isHotspotDefined = convolutionImageInformation.Defined; if (!isHotspotDefined) { MITK_ERROR << "No origin of hotspot-sphere was calculated!"; m_InternalMask = nullptr; } else { // create a binary mask around the "hotspot" region, fill the shape of a sphere around our hotspot center // typename DuplicatorType::Pointer copyMachine = DuplicatorType::New(); // copyMachine->SetInputImage(inputImage); // copyMachine->Update(); // typename CastFilterType::Pointer caster = CastFilterType::New(); // caster->SetInput( copyMachine->GetOutput() ); // caster->Update(); typename MaskImageType::Pointer hotspotMaskITK = MaskImageType::New(); hotspotMaskITK->SetOrigin(inputImage->GetOrigin()); hotspotMaskITK->SetSpacing(inputImage->GetSpacing()); hotspotMaskITK->SetLargestPossibleRegion(inputImage->GetLargestPossibleRegion()); hotspotMaskITK->SetBufferedRegion(inputImage->GetBufferedRegion()); hotspotMaskITK->SetDirection(inputImage->GetDirection()); hotspotMaskITK->SetNumberOfComponentsPerPixel(inputImage->GetNumberOfComponentsPerPixel()); hotspotMaskITK->Allocate(); hotspotMaskITK->FillBuffer(1); typedef typename InputImageType::IndexType IndexType; IndexType maskCenterIndex; for (unsigned int d =0; d< VImageDimension;++d) { maskCenterIndex[d]=convolutionImageInformation.MaxIndex[d]; } typename ConvolutionImageType::PointType maskCenter; inputImage->TransformIndexToPhysicalPoint(maskCenterIndex,maskCenter); FillHotspotMaskPixels(hotspotMaskITK.GetPointer(), maskCenter, m_HotspotRadiusinMM); //obtain mitk::Image::Pointer from itk::Image mitk::Image::Pointer hotspotMaskAsMITKImage = mitk::GrabItkImageMemory(hotspotMaskITK); m_InternalMask = hotspotMaskAsMITKImage; m_ConvolutionImageMaxIndex = convolutionImageInformation.MaxIndex; m_ConvolutionImageMinIndex = convolutionImageInformation.MinIndex; } } bool HotspotMaskGenerator::IsUpdateRequired() const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long internalMaskTimeStamp = m_InternalMask->GetMTime(); unsigned long maskGeneratorTimeStamp = m_Mask->GetMTime(); unsigned long inputImageTimeStamp = m_inputImage->GetMTime(); if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed { return true; } if (m_InternalMaskUpdateTime < maskGeneratorTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class { return true; } if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class { return true; } return false; } } diff --git a/Modules/ImageStatistics/mitkHotspotMaskGenerator.h b/Modules/ImageStatistics/mitkHotspotMaskGenerator.h index d416012f45..88ec126506 100644 --- a/Modules/ImageStatistics/mitkHotspotMaskGenerator.h +++ b/Modules/ImageStatistics/mitkHotspotMaskGenerator.h @@ -1,170 +1,169 @@ #ifndef MITKHOTSPOTCALCULATOR_H #define MITKHOTSPOTCALCULATOR_H #include #include #include #include #include #include #include #include namespace mitk { /** * @brief The HotspotMaskGenerator class is used when a hotspot has to be found in an image. A hotspot is * the region of the image where the mean intensity is maximal (=brightest spot). It is usually used in PET scans. * The identification of the hotspot is done as follows: First a cubic (or circular, if image is 2d) * mask of predefined size is generated. This mask is then convolved with the input image (in fourier domain). * The maximum value of the convolved image then corresponds to the hotspot. * If a maskGenerator is set, only the pixels of the convolved image where the corresponding mask is == @a label * are searched for the maximum value. */ class MITKIMAGESTATISTICS_EXPORT HotspotMaskGenerator: public MaskGenerator { public: /** Standard Self typedef */ typedef HotspotMaskGenerator Self; typedef MaskGenerator Superclass; typedef itk::SmartPointer< Self > Pointer; typedef itk::SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Runtime information support. */ itkTypeMacro(HotspotMaskGenerator, MaskGenerator) /** @brief Set the input image. Required for this class */ void SetInputImage(mitk::Image::Pointer inputImage); /** @brief Set a mask (can be nullptr if no mask is desired) */ void SetMask(MaskGenerator::Pointer mask); /** @brief Set the radius of the hotspot (in MM) */ void SetHotspotRadiusInMM(double radiusInMillimeter); const double& GetHotspotRadiusinMM() const; /** @brief Define whether the hotspot must be completely inside the image. Default is true */ void SetHotspotMustBeCompletelyInsideImage(bool hotspotCompletelyInsideImage); bool GetHotspotMustBeCompletelyInsideImage() const; /** @brief If a maskGenerator is set, this detemines which mask value is used */ void SetLabel(unsigned short label); /** @brief Computes and returns the hotspot mask. The hotspot mask has the same size as the input image. The hopspot has value 1, the remaining pixels are set to 0 */ mitk::Image::Pointer GetMask() override; /** @brief Returns the image index where the hotspot is located */ vnl_vector GetHotspotIndex(); /** @brief Returns the index where the convolution image is minimal (darkest spot in image) */ vnl_vector GetConvolutionImageMinIndex(); /** * @brief SetTimeStep is used to set the time step for which the mask is to be generated * @param timeStep */ void SetTimeStep(unsigned int timeStep) override; protected: HotspotMaskGenerator(); ~HotspotMaskGenerator() override; class ImageExtrema { public: bool Defined; double Max; double Min; vnl_vector MaxIndex; vnl_vector MinIndex; ImageExtrema() :Defined(false) ,Max(itk::NumericTraits::min()) ,Min(itk::NumericTraits::max()) { } }; private: /** \brief Returns size of convolution kernel depending on spacing and radius. */ template itk::Size CalculateConvolutionKernelSize(double spacing[VImageDimension], double radiusInMM); /** \brief Generates image of kernel which is needed for convolution. */ template itk::SmartPointer< itk::Image > GenerateHotspotSearchConvolutionKernel(double spacing[VImageDimension], double radiusInMM); /** \brief Convolves image with spherical kernel image. Used for hotspot calculation. */ template itk::SmartPointer< itk::Image > GenerateConvolutionImage( const itk::Image* inputImage ); /** \brief Fills pixels of the spherical hotspot mask. */ template < typename TPixel, unsigned int VImageDimension> void FillHotspotMaskPixels( itk::Image* maskImage, itk::Point sphereCenter, double sphereRadiusInMM); /** \brief */ template void CalculateHotspotMask(itk::Image* inputImage, typename itk::Image::Pointer maskImage, unsigned int label); template ImageExtrema CalculateExtremaWorld( const itk::Image* inputImage, typename itk::Image::Pointer maskImage, double neccessaryDistanceToImageBorderInMM, unsigned int label); bool IsUpdateRequired() const; HotspotMaskGenerator(const HotspotMaskGenerator &); HotspotMaskGenerator & operator=(const HotspotMaskGenerator &); MaskGenerator::Pointer m_Mask; mitk::Image::Pointer m_internalImage; itk::Image::Pointer m_internalMask2D; itk::Image::Pointer m_internalMask3D; double m_HotspotRadiusinMM; bool m_HotspotMustBeCompletelyInsideImage; - bool m_HotspotParamsChanged; unsigned short m_Label; vnl_vector m_ConvolutionImageMinIndex, m_ConvolutionImageMaxIndex; unsigned long m_InternalMaskUpdateTime; }; } #endif // MITKHOTSPOTCALCULATOR diff --git a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp index 5ee2941914..3a5dc97fdd 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp +++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp @@ -1,629 +1,630 @@ #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 "itkImageFileWriter.h" namespace mitk { void ImageStatisticsCalculator::SetInputImage(mitk::Image::Pointer image) { if (image != m_Image) { m_Image = image; m_StatisticsByTimeStep.resize(m_Image->GetTimeSteps()); m_StatisticsUpdateTimePerTimeStep.resize(m_Image->GetTimeSteps()); std::fill(m_StatisticsUpdateTimePerTimeStep.begin(), m_StatisticsUpdateTimePerTimeStep.end(), 0); this->Modified(); } } void ImageStatisticsCalculator::SetMask(mitk::MaskGenerator::Pointer mask) { if (mask != m_MaskGenerator) { m_MaskGenerator = mask; this->Modified(); } } void ImageStatisticsCalculator::SetSecondaryMask(mitk::MaskGenerator::Pointer mask) { if (mask != m_SecondaryMaskGenerator) { m_SecondaryMaskGenerator = mask; this->Modified(); } } void ImageStatisticsCalculator::SetNBinsForHistogramStatistics(unsigned int nBins) { if (nBins != m_nBinsForHistogramStatistics) { m_nBinsForHistogramStatistics = nBins; this->Modified(); this->m_UseBinSizeOverNBins = false; } if (m_UseBinSizeOverNBins) { this->Modified(); this->m_UseBinSizeOverNBins = false; } } unsigned int ImageStatisticsCalculator::GetNBinsForHistogramStatistics() const { return m_nBinsForHistogramStatistics; } void ImageStatisticsCalculator::SetBinSizeForHistogramStatistics(double binSize) { if (binSize != m_binSizeForHistogramStatistics) { m_binSizeForHistogramStatistics = binSize; this->Modified(); this->m_UseBinSizeOverNBins = true; } if (!m_UseBinSizeOverNBins) { this->Modified(); this->m_UseBinSizeOverNBins = true; } } double ImageStatisticsCalculator::GetBinSizeForHistogramStatistics() const { return m_binSizeForHistogramStatistics; } ImageStatisticsCalculator::StatisticsContainer::Pointer ImageStatisticsCalculator::GetStatistics(unsigned int timeStep, unsigned int label) { if (timeStep >= m_StatisticsByTimeStep.size()) { mitkThrow() << "invalid timeStep in ImageStatisticsCalculator_v2::GetStatistics"; } if (m_Image.IsNull()) { mitkThrow() << "no image"; } if (!m_Image->IsInitialized()) { mitkThrow() << "Image not initialized!"; } if (IsUpdateRequired(timeStep)) { if (m_MaskGenerator.IsNotNull()) { m_MaskGenerator->SetTimeStep(timeStep); m_InternalMask = m_MaskGenerator->GetMask(); if (m_MaskGenerator->GetReferenceImage().IsNotNull()) { m_InternalImageForStatistics = m_MaskGenerator->GetReferenceImage(); } else { m_InternalImageForStatistics = m_Image; } } else { m_InternalImageForStatistics = m_Image; } if (m_SecondaryMaskGenerator.IsNotNull()) { m_SecondaryMaskGenerator->SetTimeStep(timeStep); m_SecondaryMask = m_SecondaryMaskGenerator->GetMask(); } ImageTimeSelector::Pointer imgTimeSel = ImageTimeSelector::New(); imgTimeSel->SetInput(m_InternalImageForStatistics); imgTimeSel->SetTimeNr(timeStep); imgTimeSel->UpdateLargestPossibleRegion(); m_ImageTimeSlice = imgTimeSel->GetOutput(); // Calculate statistics with/without mask if (m_MaskGenerator.IsNull() && m_SecondaryMaskGenerator.IsNull()) { // 1) calculate statistics unmasked: AccessByItk_1(m_ImageTimeSlice, InternalCalculateStatisticsUnmasked, timeStep) } else { // 2) calculate statistics masked AccessByItk_1(m_ImageTimeSlice, InternalCalculateStatisticsMasked, timeStep) } //this->Modified(); } m_StatisticsUpdateTimePerTimeStep[timeStep] = m_StatisticsByTimeStep[timeStep][m_StatisticsByTimeStep[timeStep].size()-1]->GetMTime(); for (auto it = m_StatisticsByTimeStep[timeStep].begin(); it != m_StatisticsByTimeStep[timeStep].end(); ++it) { StatisticsContainer::Pointer statCont = *it; if (statCont->GetLabel() == label) { return statCont->Clone(); } } // these lines will ony be executed if the requested label could not be found! MITK_WARN << "Invalid label: " << label << " in time step: " << timeStep; return StatisticsContainer::New(); } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateStatisticsUnmasked( typename itk::Image< TPixel, VImageDimension >* image, unsigned int timeStep) { typedef typename itk::Image< TPixel, VImageDimension > ImageType; typedef typename itk::ExtendedStatisticsImageFilter ImageStatisticsFilterType; typedef typename itk::MinMaxImageFilterWithIndex MinMaxFilterType; StatisticsContainer::Pointer statisticsResult = StatisticsContainer::New(); typename ImageStatisticsFilterType::Pointer statisticsFilter = ImageStatisticsFilterType::New(); statisticsFilter->SetInput(image); statisticsFilter->SetCoordinateTolerance(0.001); statisticsFilter->SetDirectionTolerance(0.001); // TODO: this is single threaded. Implement our own image filter that does this multi threaded // typename itk::MinimumMaximumImageCalculator::Pointer imgMinMaxFilter = itk::MinimumMaximumImageCalculator::New(); // imgMinMaxFilter->SetImage(image); // imgMinMaxFilter->Compute(); vnl_vector minIndex, maxIndex; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetInput(image); minMaxFilter->UpdateLargestPossibleRegion(); typename ImageType::PixelType minval = minMaxFilter->GetMin(); typename ImageType::PixelType maxval = minMaxFilter->GetMax(); typename ImageType::IndexType tmpMinIndex = minMaxFilter->GetMinIndex(); typename ImageType::IndexType tmpMaxIndex = minMaxFilter->GetMaxIndex(); // typename ImageType::IndexType tmpMinIndex = imgMinMaxFilter->GetIndexOfMinimum(); // typename ImageType::IndexType tmpMaxIndex = imgMinMaxFilter->GetIndexOfMaximum(); minIndex.set_size(tmpMaxIndex.GetIndexDimension()); maxIndex.set_size(tmpMaxIndex.GetIndexDimension()); for (unsigned int i=0; i < tmpMaxIndex.GetIndexDimension(); i++) { minIndex[i] = tmpMinIndex[i]; maxIndex[i] = tmpMaxIndex[i]; } statisticsResult->SetMinIndex(minIndex); statisticsResult->SetMaxIndex(maxIndex); //convert m_binSize in m_nBins if necessary unsigned int nBinsForHistogram; if (m_UseBinSizeOverNBins) { nBinsForHistogram = std::max(static_cast(std::ceil(maxval - minval)) / m_binSizeForHistogramStatistics, 10.); // do not allow less than 10 bins } else { nBinsForHistogram = m_nBinsForHistogramStatistics; } statisticsFilter->SetHistogramParameters(nBinsForHistogram, minval, maxval); try { statisticsFilter->Update(); } catch (const itk::ExceptionObject& e) { mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what(); } // no mask, therefore just one label = the whole image m_StatisticsByTimeStep[timeStep].resize(1); statisticsResult->SetLabel(1); statisticsResult->SetN(image->GetLargestPossibleRegion().GetNumberOfPixels()); statisticsResult->SetMean(statisticsFilter->GetMean()); statisticsResult->SetMin(statisticsFilter->GetMinimum()); statisticsResult->SetMax(statisticsFilter->GetMaximum()); statisticsResult->SetVariance(statisticsFilter->GetVariance()); statisticsResult->SetStd(statisticsFilter->GetSigma()); statisticsResult->SetSkewness(statisticsFilter->GetSkewness()); statisticsResult->SetKurtosis(statisticsFilter->GetKurtosis()); statisticsResult->SetRMS(std::sqrt(std::pow(statisticsFilter->GetMean(), 2.) + statisticsFilter->GetVariance())); // variance = sigma^2 statisticsResult->SetMPP(statisticsFilter->GetMPP()); statisticsResult->SetEntropy(statisticsFilter->GetEntropy()); statisticsResult->SetMedian(statisticsFilter->GetMedian()); statisticsResult->SetUniformity(statisticsFilter->GetUniformity()); statisticsResult->SetUPP(statisticsFilter->GetUPP()); statisticsResult->SetHistogram(statisticsFilter->GetHistogram()); m_StatisticsByTimeStep[timeStep][0] = statisticsResult; } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateStatisticsMasked( typename itk::Image< TPixel, VImageDimension >* image, unsigned int timeStep) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< MaskPixelType, VImageDimension > MaskType; typedef typename MaskType::PixelType LabelPixelType; typedef itk::ExtendedLabelStatisticsImageFilter< ImageType, MaskType > ImageStatisticsFilterType; typedef MaskUtilities< TPixel, VImageDimension > MaskUtilType; typedef typename itk::MinMaxLabelImageFilterWithIndex MinMaxLabelFilterType; typedef typename ImageType::PixelType InputImgPixelType; // workaround: if m_SecondaryMaskGenerator ist not null but m_MaskGenerator is! (this is the case if we request a 'ignore zuero valued pixels' // mask in the gui but do not define a primary mask) bool swapMasks = false; if (m_SecondaryMask.IsNotNull() && m_InternalMask.IsNull()) { m_InternalMask = m_SecondaryMask; m_SecondaryMask = nullptr; swapMasks = true; } // maskImage has to have the same dimension as image typename MaskType::Pointer maskImage = MaskType::New(); try { // try to access the pixel values directly (no copying or casting). Only works if mask pixels are of pixelType unsigned short maskImage = ImageToItkImage< MaskPixelType, VImageDimension >(m_InternalMask); } catch (const itk::ExceptionObject &) { // if the pixel type of the mask is not short, then we have to make a copy of m_InternalMask (and cast the values) CastToItkImage(m_InternalMask, maskImage); } // if we have a secondary mask (say a ignoreZeroPixelMask) we need to combine the masks (corresponds to AND) if (m_SecondaryMask.IsNotNull()) { // dirty workaround for a bug when pf mask + any other mask is used in conjunction. We need a proper fix for this (Fabian Isensee is responsible and probably working on it!) if (m_InternalMask->GetDimension() == 2 && (m_SecondaryMask->GetDimension() == 3 || m_SecondaryMask->GetDimension() == 4)) { mitk::Image::Pointer old_img = m_SecondaryMaskGenerator->GetReferenceImage(); m_SecondaryMaskGenerator->SetInputImage(m_MaskGenerator->GetReferenceImage()); m_SecondaryMask = m_SecondaryMaskGenerator->GetMask(); m_SecondaryMaskGenerator->SetInputImage(old_img); } typename MaskType::Pointer secondaryMaskImage = MaskType::New(); secondaryMaskImage = ImageToItkImage< MaskPixelType, VImageDimension >(m_SecondaryMask); // secondary mask should be a ignore zero value pixel mask derived from image. it has to be cropped to the mask region (which may be planar or simply smaller) typename MaskUtilities::Pointer secondaryMaskMaskUtil = MaskUtilities::New(); secondaryMaskMaskUtil->SetImage(secondaryMaskImage.GetPointer()); secondaryMaskMaskUtil->SetMask(maskImage.GetPointer()); typename MaskType::Pointer adaptedSecondaryMaskImage = secondaryMaskMaskUtil->ExtractMaskImageRegion(); typename itk::MaskImageFilter2::Pointer maskFilter = itk::MaskImageFilter2::New(); maskFilter->SetInput1(maskImage); maskFilter->SetInput2(adaptedSecondaryMaskImage); maskFilter->SetMaskingValue(1); // all pixels of maskImage where secondaryMaskImage==1 will be kept, all the others are set to 0 maskFilter->UpdateLargestPossibleRegion(); maskImage = maskFilter->GetOutput(); } typename MaskUtilType::Pointer maskUtil = MaskUtilType::New(); maskUtil->SetImage(image); maskUtil->SetMask(maskImage.GetPointer()); // if mask is smaller than image, extract the image region where the mask is typename ImageType::Pointer adaptedImage = ImageType::New(); adaptedImage = maskUtil->ExtractMaskImageRegion(); // this also checks mask sanity // find min, max, minindex and maxindex typename MinMaxLabelFilterType::Pointer minMaxFilter = MinMaxLabelFilterType::New(); minMaxFilter->SetInput(adaptedImage); minMaxFilter->SetLabelInput(maskImage); minMaxFilter->UpdateLargestPossibleRegion(); // set histogram parameters for each label individually (min/max may be different for each label) typedef typename std::map MapType; typedef typename std::pair PairType; std::vector relevantLabels = minMaxFilter->GetRelevantLabels(); MapType minVals; MapType maxVals; std::map nBins; for (LabelPixelType label:relevantLabels) { minVals.insert(PairType(label, minMaxFilter->GetMin(label))); maxVals.insert(PairType(label, minMaxFilter->GetMax(label))); unsigned int nBinsForHistogram; if (m_UseBinSizeOverNBins) { nBinsForHistogram = std::max(static_cast(std::ceil(minMaxFilter->GetMax(label) - minMaxFilter->GetMin(label))) / m_binSizeForHistogramStatistics, 10.); // do not allow less than 10 bins } else { nBinsForHistogram = m_nBinsForHistogramStatistics; } nBins.insert(typename std::pair(label, nBinsForHistogram)); } typename ImageStatisticsFilterType::Pointer imageStatisticsFilter = ImageStatisticsFilterType::New(); imageStatisticsFilter->SetDirectionTolerance(0.001); imageStatisticsFilter->SetCoordinateTolerance(0.001); imageStatisticsFilter->SetInput(adaptedImage); imageStatisticsFilter->SetLabelInput(maskImage); imageStatisticsFilter->SetHistogramParametersForLabels(nBins, minVals, maxVals); imageStatisticsFilter->Update(); std::list labels = imageStatisticsFilter->GetRelevantLabels(); auto it = labels.begin(); m_StatisticsByTimeStep[timeStep].resize(0); while(it != labels.end()) { StatisticsContainer::Pointer statisticsResult = StatisticsContainer::New(); // find min, max, minindex and maxindex // make sure to only look in the masked region, use a masker for this vnl_vector minIndex, maxIndex; mitk::Point3D worldCoordinateMin; mitk::Point3D worldCoordinateMax; mitk::Point3D indexCoordinateMin; mitk::Point3D indexCoordinateMax; m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMinIndex(*it), worldCoordinateMin); m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMaxIndex(*it), worldCoordinateMax); m_Image->GetGeometry()->WorldToIndex(worldCoordinateMin, indexCoordinateMin); m_Image->GetGeometry()->WorldToIndex(worldCoordinateMax, indexCoordinateMax); minIndex.set_size(3); maxIndex.set_size(3); //for (unsigned int i=0; i < tmpMaxIndex.GetIndexDimension(); i++) for (unsigned int i=0; i < 3; i++) { minIndex[i] = indexCoordinateMin[i]; maxIndex[i] = indexCoordinateMax[i]; } statisticsResult->SetMinIndex(minIndex); statisticsResult->SetMaxIndex(maxIndex); assert(std::abs(minMaxFilter->GetMax(*it) - imageStatisticsFilter->GetMaximum(*it)) < mitk::eps); assert(std::abs(minMaxFilter->GetMin(*it) - imageStatisticsFilter->GetMinimum(*it)) < mitk::eps); statisticsResult->SetN(imageStatisticsFilter->GetSum(*it) / (double) imageStatisticsFilter->GetMean(*it)); statisticsResult->SetMean(imageStatisticsFilter->GetMean(*it)); statisticsResult->SetMin(imageStatisticsFilter->GetMinimum(*it)); statisticsResult->SetMax(imageStatisticsFilter->GetMaximum(*it)); statisticsResult->SetVariance(imageStatisticsFilter->GetVariance(*it)); statisticsResult->SetStd(imageStatisticsFilter->GetSigma(*it)); statisticsResult->SetSkewness(imageStatisticsFilter->GetSkewness(*it)); statisticsResult->SetKurtosis(imageStatisticsFilter->GetKurtosis(*it)); statisticsResult->SetRMS(std::sqrt(std::pow(imageStatisticsFilter->GetMean(*it), 2.) + imageStatisticsFilter->GetVariance(*it))); // variance = sigma^2 statisticsResult->SetMPP(imageStatisticsFilter->GetMPP(*it)); statisticsResult->SetLabel(*it); statisticsResult->SetEntropy(imageStatisticsFilter->GetEntropy(*it)); statisticsResult->SetMedian(imageStatisticsFilter->GetMedian(*it)); statisticsResult->SetUniformity(imageStatisticsFilter->GetUniformity(*it)); statisticsResult->SetUPP(imageStatisticsFilter->GetUPP(*it)); statisticsResult->SetHistogram(imageStatisticsFilter->GetHistogram(*it)); m_StatisticsByTimeStep[timeStep].push_back(statisticsResult); ++it; } // swap maskGenerators back if (swapMasks) { m_SecondaryMask = m_InternalMask; m_InternalMask = nullptr; } } bool ImageStatisticsCalculator::IsUpdateRequired(unsigned int timeStep) const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long inputImageTimeStamp = m_Image->GetMTime(); unsigned long statisticsTimeStamp = m_StatisticsUpdateTimePerTimeStep[timeStep]; if (thisClassTimeStamp > statisticsTimeStamp) // inputs have changed { return true; } if (inputImageTimeStamp > statisticsTimeStamp) // image has changed { return true; } if (m_MaskGenerator.IsNotNull()) { unsigned long maskGeneratorTimeStamp = m_MaskGenerator->GetMTime(); if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a mask generator and it has changed { return true; } } if (m_SecondaryMaskGenerator.IsNotNull()) { unsigned long maskGeneratorTimeStamp = m_SecondaryMaskGenerator->GetMTime(); if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a secondary mask generator and it has changed { return true; } } return false; } ImageStatisticsCalculator::StatisticsContainer::StatisticsContainer(): m_N(0), m_Mean(nan("")), m_Min(nan("")), m_Max(nan("")), m_Std(nan("")), m_Variance(nan("")), m_Skewness(nan("")), m_Kurtosis(nan("")), m_RMS(nan("")), m_MPP(nan("")), m_Median(nan("")), m_Uniformity(nan("")), m_UPP(nan("")), - m_Entropy(nan("")) + m_Entropy(nan("")), + m_Label(0) { m_minIndex.set_size(0); m_maxIndex.set_size(0); } ImageStatisticsCalculator::statisticsMapType ImageStatisticsCalculator::StatisticsContainer::GetStatisticsAsMap() { ImageStatisticsCalculator::statisticsMapType statisticsAsMap; statisticsAsMap["N"] = m_N; statisticsAsMap["Mean"] = m_Mean; statisticsAsMap["Min"] = m_Min; statisticsAsMap["Max"] = m_Max; statisticsAsMap["StandardDeviation"] = m_Std; statisticsAsMap["Variance"] = m_Variance; statisticsAsMap["Skewness"] = m_Skewness; statisticsAsMap["Kurtosis"] = m_Kurtosis; statisticsAsMap["RMS"] = m_RMS; statisticsAsMap["MPP"] = m_MPP; statisticsAsMap["Median"] = m_Median; statisticsAsMap["Uniformity"] = m_Uniformity; statisticsAsMap["UPP"] = m_UPP; statisticsAsMap["Entropy"] = m_Entropy; statisticsAsMap["Label"] = m_Label; return statisticsAsMap; } void ImageStatisticsCalculator::StatisticsContainer::Reset() { m_N = 0; m_Mean = nan(""); m_Min = nan(""); m_Max = nan(""); m_Std = nan(""); m_Variance = nan(""); m_Skewness = nan(""); m_Kurtosis = nan(""); m_RMS = nan(""); m_MPP = nan(""); m_Median = nan(""); m_Uniformity = nan(""); m_UPP = nan(""); m_Entropy = nan(""); m_Histogram = HistogramType::New(); m_minIndex.set_size(0); m_maxIndex.set_size(0); m_Label = 0; } void ImageStatisticsCalculator::StatisticsContainer::Print() { ImageStatisticsCalculator::statisticsMapType statMap = this->GetStatisticsAsMap(); // print all map key value pairs // const auto& val:statMap for (auto it = statMap.begin(); it != statMap.end(); ++it) { std::cout << it->first << ": " << it->second << std::endl; } // print the min and max index std::cout << "Min Index:" << std::endl; for (auto it = this->GetMinIndex().begin(); it != this->GetMinIndex().end(); ++it) { std::cout << *it << " "; } std::cout << std::endl; // print the min and max index std::cout << "Max Index:" << std::endl; for (auto it = this->GetMaxIndex().begin(); it != this->GetMaxIndex().end(); ++it) { std::cout << *it << " "; } std::cout << std::endl; } std::string ImageStatisticsCalculator::StatisticsContainer::GetAsString() { std::string res = ""; ImageStatisticsCalculator::statisticsMapType statMap = this->GetStatisticsAsMap(); // print all map key value pairs // const auto& val:statMap for (auto it = statMap.begin(); it != statMap.end(); ++it) { res += std::string(it->first) + ": " + std::to_string(it->second) + "\n"; } // print the min and max index res += "Min Index:" + std::string("\n"); for (auto it = this->GetMinIndex().begin(); it != this->GetMinIndex().end(); it++) { res += std::to_string(*it) + std::string(" "); } res += "\n"; // print the min and max index res += "Max Index:" + std::string("\n"); for (auto it = this->GetMaxIndex().begin(); it != this->GetMaxIndex().end(); it++) { res += std::to_string(*it) + " "; } res += "\n"; return res; } } diff --git a/Modules/ImageStatistics/mitkMaskUtilities.h b/Modules/ImageStatistics/mitkMaskUtilities.h index 73bd79c187..79d93ab86b 100644 --- a/Modules/ImageStatistics/mitkMaskUtilities.h +++ b/Modules/ImageStatistics/mitkMaskUtilities.h @@ -1,69 +1,69 @@ #ifndef MITKMASKUTIL #define MITKMASKUTIL #include #include #include namespace mitk { /** * @brief Utility class for mask operations. It checks whether an image and a mask are compatible (spacing, orientation, etc...) * and it can also crop an image to the LargestPossibleRegion of the Mask */ template class MITKIMAGESTATISTICS_EXPORT MaskUtilities: public itk::Object { public: /** Standard Self typedef */ typedef MaskUtilities Self; typedef itk::Object Superclass; typedef itk::SmartPointer< Self > Pointer; typedef itk::SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Runtime information support. */ itkTypeMacro(MaskUtilities, itk::Object) typedef itk::Image ImageType; typedef itk::Image MaskType; /** * @brief Set image */ void SetImage(ImageType* image); /** * @brief Set mask */ void SetMask(MaskType* mask); /** * @brief Checks whether mask and image are compatible for joint access (as via iterators). * Spacing and direction must be the same between the two and they must be aligned. Also, the mask must be completely inside the image */ bool CheckMaskSanity(); /** * @brief Crops the image to the LargestPossibleRegion of the mask */ typename itk::Image::Pointer ExtractMaskImageRegion(); protected: - MaskUtilities(){} + MaskUtilities(): m_Image(nullptr), m_Mask(nullptr){} ~MaskUtilities() override{} private: itk::Image* m_Image; itk::Image* m_Mask; }; } #ifndef ITK_MANUAL_INSTANTIATION #include #endif #endif diff --git a/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx b/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx index c925cb5aa4..77b4510f4f 100644 --- a/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx +++ b/Modules/ImageStatistics/mitkMinMaxImageFilterWithIndex.hxx @@ -1,106 +1,109 @@ #ifndef MITK_MinMaxImageFilterWithIndex_HXX #define MITK_MinMaxImageFilterWithIndex_HXX #include #include namespace itk { template< typename TInputImage > void MinMaxImageFilterWithIndex< TInputImage >::AllocateOutputs() { // Pass the input through as the output typename TInputImage::Pointer image = const_cast< TInputImage * >( this->GetInput() ); this->GraftOutput(image); // Nothing that needs to be allocated for the remaining outputs } template< typename TInputImage > void MinMaxImageFilterWithIndex< TInputImage >::ThreadedGenerateData(const RegionType & outputRegionForThread, ThreadIdType threadId) { const SizeValueType size0 = outputRegionForThread.GetSize(0); if( size0 == 0) { return; } PixelType value; PixelType threadMin, threadMax; - IndexType threadMinIndex, threadMaxIndex; + IndexType threadMinIndex; + IndexType threadMaxIndex; + threadMinIndex.Fill(0); + threadMaxIndex.Fill(0); threadMin = std::numeric_limits::max(); threadMax = std::numeric_limits::min(); ImageRegionConstIteratorWithIndex< TInputImage > it (this->GetInput(), outputRegionForThread); // do the work while ( !it.IsAtEnd() ) { value = it.Get(); if (value < threadMin) { threadMin = value; threadMinIndex = it.GetIndex(); } if (value > threadMax) { threadMax = value; threadMaxIndex = it.GetIndex(); } ++it; } m_ThreadMax[threadId] = threadMax; m_ThreadMin[threadId] = threadMin; m_ThreadMaxIndex[threadId] = threadMaxIndex; m_ThreadMinIndex[threadId] = threadMinIndex; } template< typename TInputImage > void MinMaxImageFilterWithIndex< TInputImage >::BeforeThreadedGenerateData() { ThreadIdType numberOfThreads = this->GetNumberOfThreads(); m_ThreadMin.resize(numberOfThreads); m_ThreadMax.resize(numberOfThreads); m_ThreadMinIndex.resize(numberOfThreads); m_ThreadMaxIndex.resize(numberOfThreads); for (unsigned int i =0; i < numberOfThreads; i++) { m_ThreadMin[i] = std::numeric_limits::max(); m_ThreadMax[i] = std::numeric_limits::min(); } m_Min = std::numeric_limits::max(); m_Max = std::numeric_limits::min(); } template< typename TInputImage > void MinMaxImageFilterWithIndex< TInputImage >::AfterThreadedGenerateData() { ThreadIdType numberOfThreads = this->GetNumberOfThreads(); for (ThreadIdType i = 0; i < numberOfThreads; i++) { if (m_ThreadMin[i] < m_Min) { m_Min = m_ThreadMin[i]; m_MinIndex = m_ThreadMinIndex[i]; } if (m_ThreadMax[i] > m_Max) { m_Max = m_ThreadMax[i]; m_MaxIndex = m_ThreadMaxIndex[i]; } } } } #endif diff --git a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h index f28688f3e3..dcc30b9e70 100644 --- a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h +++ b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.h @@ -1,131 +1,132 @@ #ifndef MITKPLANARFIGUREMASKGENERATOR #define MITKPLANARFIGUREMASKGENERATOR #include #include #include #include #include #include #include #include #include #include namespace mitk { /** * \class PlanarFigureMaskGenerator * \brief Derived from MaskGenerator. This class is used to convert a mitk::PlanarFigure into a binary image mask */ class MITKIMAGESTATISTICS_EXPORT PlanarFigureMaskGenerator: public MaskGenerator { public: /** Standard Self typedef */ typedef PlanarFigureMaskGenerator Self; typedef MaskGenerator Superclass; typedef itk::SmartPointer< Self > Pointer; typedef itk::SmartPointer< const Self > ConstPointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Runtime information support. */ itkTypeMacro(PlanarFigureMaskGenerator, MaskGenerator) /** * @brief GetMask Computes and returns the mask * @return mitk::Image::Pointer of the generated mask */ mitk::Image::Pointer GetMask() override; void SetPlanarFigure(mitk::PlanarFigure::Pointer planarFigure); mitk::Image::Pointer GetReferenceImage() override; /** * @brief SetTimeStep is used to set the time step for which the mask is to be generated * @param timeStep */ void SetTimeStep(unsigned int timeStep) override; protected: PlanarFigureMaskGenerator():Superclass(){ m_InternalMaskUpdateTime = 0; m_InternalMask = mitk::Image::New(); m_ReferenceImage = nullptr; + m_PlanarFigureAxis = 0; } private: void CalculateMask(); template < typename TPixel, unsigned int VImageDimension > void InternalCalculateMaskFromPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ); template < typename TPixel, unsigned int VImageDimension > void InternalCalculateMaskFromOpenPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ); mitk::Image::Pointer extract2DImageSlice(unsigned int axis, unsigned int slice); bool GetPrincipalAxis(const BaseGeometry *geometry, Vector3D vector, unsigned int &axis ); /** Connection from ITK to VTK */ template void ConnectPipelines(ITK_Exporter exporter, vtkSmartPointer importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } /** Connection from VTK to ITK */ template void ConnectPipelines(vtkSmartPointer exporter, ITK_Importer importer) { importer->SetUpdateInformationCallback(exporter->GetUpdateInformationCallback()); importer->SetPipelineModifiedCallback(exporter->GetPipelineModifiedCallback()); importer->SetWholeExtentCallback(exporter->GetWholeExtentCallback()); importer->SetSpacingCallback(exporter->GetSpacingCallback()); importer->SetOriginCallback(exporter->GetOriginCallback()); importer->SetScalarTypeCallback(exporter->GetScalarTypeCallback()); importer->SetNumberOfComponentsCallback(exporter->GetNumberOfComponentsCallback()); importer->SetPropagateUpdateExtentCallback(exporter->GetPropagateUpdateExtentCallback()); importer->SetUpdateDataCallback(exporter->GetUpdateDataCallback()); importer->SetDataExtentCallback(exporter->GetDataExtentCallback()); importer->SetBufferPointerCallback(exporter->GetBufferPointerCallback()); importer->SetCallbackUserData(exporter->GetCallbackUserData()); } bool IsUpdateRequired() const; mitk::PlanarFigure::Pointer m_PlanarFigure; itk::Image::Pointer m_InternalITKImageMask2D; mitk::Image::Pointer m_InternalTimeSliceImage; mitk::Image::Pointer m_ReferenceImage; unsigned int m_PlanarFigureAxis; unsigned long m_InternalMaskUpdateTime; }; } #endif // MITKPLANARFIGUREMASKGENERATOR diff --git a/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.h b/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.h index c612d2e152..9495c501b7 100644 --- a/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.h +++ b/Modules/ImageStatistics/mitkPointSetStatisticsCalculator.h @@ -1,122 +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 _MITK_PointSetSTATISTICSCALCULATOR_H #define _MITK_PointSetSTATISTICSCALCULATOR_H #include #include #include namespace mitk { /** * \brief Class for calculating statistics (like standard derivation, RMS, mean, etc.) for a PointSet. */ class MITKIMAGESTATISTICS_EXPORT PointSetStatisticsCalculator : public itk::Object { public: mitkClassMacroItkParent( PointSetStatisticsCalculator, itk::Object ); itkFactorylessNewMacro(Self) itkCloneMacro(Self) mitkNewMacro1Param(PointSetStatisticsCalculator,mitk::PointSet::Pointer); /** @brief Sets the point set which will be analysed. */ void SetPointSet(mitk::PointSet::Pointer pSet); /** @return Returns the mean position of the analysed point set (only valid navigation data). Returns [0;0;0] if there is no valid navigation data.*/ mitk::Point3D GetPositionMean(); /** @return Returns the standard derivation of each component (x, y and z) of the analysed point set (only valid navigation data). Returns [0;0;0] if there is no valid navigation data.*/ mitk::Vector3D GetPositionStandardDeviation(); /** @return Returns the sample standard derivation of each component (x, y and z) of the analysed point set (only valid navigation data). Returns [0;0;0] if there is no valid navigation data.*/ mitk::Vector3D GetPositionSampleStandardDeviation(); /** @return Returns the mean distance to the mean postion (=mean error) of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */ double GetPositionErrorMean(); /** @return Returns the standard derivation of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */ double GetPositionErrorStandardDeviation(); /** @return Returns the sample standard derivation of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */ double GetPositionErrorSampleStandardDeviation(); /** @return Returns the RMS of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */ double GetPositionErrorRMS(); /** @return Returns the median of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */ double GetPositionErrorMedian(); /** @return Returns the maximum of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */ double GetPositionErrorMax(); /** @return Returns the minimum of the errors of all positions of the analysed point set (only valid navigation data). Returns 0 if there is no valid navigation data. */ double GetPositionErrorMin(); //##################################################################################################### //this both methods are used by another class an so they are public... perhaps we want to move them //out of this class because they have nothing to do with point sets. /** @return returns the standard derivation of the given list (NOT of the point set).*/ double GetStabw(std::vector list); /** @return returns the sample standard derivation of the given list (NOT of the point set).*/ double GetSampleStabw(std::vector list); //##################################################################################################### protected: PointSetStatisticsCalculator(); - PointSetStatisticsCalculator(mitk::PointSet::Pointer); + explicit PointSetStatisticsCalculator(mitk::PointSet::Pointer); ~PointSetStatisticsCalculator() override; // TODO: Remove the std::vector data structure and use mitk::PointSet everywhere /** @return Returns a list with the distances to the mean of the list */ std::vector GetErrorList(std::vector list); /** @return Returns the mean of the point list. Returns [0;0;0] if the list is empty. */ mitk::Point3D GetMean(std::vector list); /** @brief Converts a point set to a vector of Point3D. */ std::vector PointSetToVector(mitk::PointSet::Pointer pSet); /** @return Returns true if all positions in the evaluated points set are equal. False if not. */ bool CheckIfAllPositionsAreEqual(); mitk::PointSet::Pointer m_PointSet; double GetMean(std::vector list); double GetMedian(std::vector list); double GetMax(std::vector list); double GetMin(std::vector list); }; } #endif // #define _MITK_PointSetSTATISTICSCALCULATOR_H diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.denoising/src/internal/QmitkDenoisingViewControls.ui b/Plugins/org.mitk.gui.qt.diffusionimaging.denoising/src/internal/QmitkDenoisingViewControls.ui index 72891457e8..35da1a192a 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.denoising/src/internal/QmitkDenoisingViewControls.ui +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.denoising/src/internal/QmitkDenoisingViewControls.ui @@ -1,323 +1,329 @@ QmitkDenoisingViewControls 0 0 351 734 0 0 QmitkTemplate 6 9 9 9 9 QFrame::NoFrame QFrame::Raised 0 0 0 0 Input Image: Filter: Parameters 0 0 0 0 Lambda: + + 4 + 0.000000000000000 1.000000000000000 0.100000000000000 0.100000000000000 Iterations: 1 999 1 Parameters 0 0 0 0 Sampling Radius: 1 999 10 1 999 4 1 999 4 Num. Patches: 1 50 1 Iterations: Patch Size: Parameters 0 0 0 0 + + 4 + 1.000000000000000 99999.000000000000000 0.100000000000000 1.000000000000000 Variance: Start Qt::Vertical 20 40 QmitkDataStorageComboBox QComboBox
QmitkDataStorageComboBox.h
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 26310fa9ab..0fd430d313 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,1962 +1,1962 @@ /*=================================================================== 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.preprocessing"; #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; } - target_image->SetOrigin(image->GetGeometry()->GetOrigin()); - mitk::RenderingManager::GetInstance()->InitializeViews( target_image->GetTimeGeometry(), + 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(); GradProp::GradientDirectionsContainerType::Pointer oldGradients = static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(); GradProp::GradientDirectionsContainerType::Pointer 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); newImage->GetPropertyList()->ReplaceProperty( PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str(), GradProp::New( newGradients ) ); PropHelper propertyHelper( newImage ); propertyHelper.InitializeImage(); 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( static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer() ); filter->Update(); mitk::Image::Pointer newImage = mitk::GrabItkImageMemory( filter->GetOutput() ); PropHelper::CopyProperties(image, newImage, true); newImage->GetPropertyList()->ReplaceProperty( PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str(), GradProp::New( filter->GetNewDirections() ) ); PropHelper propertyHelper( newImage ); propertyHelper.InitializeImage(); 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 propertyHelper( newimage ); propertyHelper.InitializeImage(); 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 propertyHelper( newImage ); propertyHelper.InitializeImage(); 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(); } } newImage->GetPropertyList()->ReplaceProperty( PropHelper::MEASUREMENTFRAMEPROPERTYNAME.c_str(), mitk::MeasurementFrameProperty::New( mf ) ); PropHelper propertyHelper( newImage ); propertyHelper.InitializeImage(); } 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; } GradientDirectionContainerType::Pointer gradientContainer = static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(); 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( static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer() ); 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 propertyHelper( newImage ); propertyHelper.InitializeImage(); 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; } bool isDiffusionImage( PropHelper::IsDiffusionWeightedImage(image) ); if ( ! isDiffusionImage ) { return; } typedef itk::DwiGradientLengthCorrectionFilter FilterType; ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); FilterType::Pointer filter = FilterType::New(); filter->SetRoundingValue( m_Controls->m_B_ValueMap_Rounder_SpinBox->value()); filter->SetReferenceBValue( static_cast (image->GetProperty(PropHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue() ); filter->SetReferenceGradientDirectionContainer( static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer() ); 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); newImage->GetPropertyList()->ReplaceProperty( PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str(), GradProp::New( filter->GetOutputGradientDirectionContainer() ) ); newImage->GetPropertyList()->ReplaceProperty( PropHelper::REFERENCEBVALUEPROPERTYNAME.c_str(), mitk::FloatProperty::New( filter->GetNewBValue() ) ); PropHelper propertyHelper( newImage ); propertyHelper.InitializeImage(); 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); } 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 = static_cast (image->GetProperty(PropHelper::BVALUEMAPPROPERTYNAME.c_str()).GetPointer() ) ->GetBValueMap(); ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image, itkVectorImagePointer); ItkDwiType* vectorImage = itkVectorImagePointer.GetPointer(); const GradProp::GradientDirectionsContainerType::Pointer gradientContainer = static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(); const unsigned int& bValue = static_cast (image->GetProperty(PropHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue(); 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); outImage->GetPropertyList()->ReplaceProperty( PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str(), GradProp::New( filter->GetTargetGradientDirections() ) ); outImage->GetPropertyList()->ReplaceProperty( PropHelper::REFERENCEBVALUEPROPERTYNAME.c_str(), mitk::FloatProperty::New( m_Controls->m_targetBValueSpinBox->value() ) ); PropHelper propertyHelper( outImage ); propertyHelper.InitializeImage(); imageNode = mitk::DataNode::New(); imageNode->SetData( outImage ); imageNode->SetName(imageName.toStdString().c_str()); GetDataStorage()->Add(imageNode, parent); // if(m_Controls->m_OutputRMSErrorImage->isChecked()){ // // create new Error image // FilterType::ErrorImageType::Pointer errImage = filter->GetErrorImage(); // mitk::Image::Pointer mitkErrImage = mitk::Image::New(); // mitkErrImage->InitializeByItk(errImage); // mitkErrImage->SetVolume(errImage->GetBufferPointer()); // imageNode = mitk::DataNode::New(); // imageNode->SetData( mitkErrImage ); // imageNode->SetName((imageName+"_Error").toStdString().c_str()); // GetDataStorage()->Add(imageNode); // } } 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 = static_cast (image->GetProperty(PropHelper::BVALUEMAPPROPERTYNAME.c_str()).GetPointer() ) ->GetBValueMap(); 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 = static_cast (image->GetProperty(PropHelper::BVALUEMAPPROPERTYNAME.c_str()).GetPointer() ) ->GetBValueMap(); 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 = static_cast(image->GetProperty(PropHelper::BVALUEMAPPROPERTYNAME.c_str()).GetPointer() )->GetBValueMap(); 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::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 = static_cast (image->GetProperty(PropHelper::BVALUEMAPPROPERTYNAME.c_str()).GetPointer() ) ->GetBValueMap(); 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()); 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 = static_cast (image->GetProperty(PropHelper::MEASUREMENTFRAMEPROPERTYNAME.c_str()).GetPointer() ) ->GetMeasurementFrame(); 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 = static_cast (image->GetProperty(PropHelper::BVALUEMAPPROPERTYNAME.c_str()).GetPointer() ) ->GetBValueMap(); 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(static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer()->Size()-1); m_Controls->m_ExtractGradientBox->setMaximum(static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer()->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 propertyHelper( newDwi ); propertyHelper.InitializeImage(); PropHelper::ClearMeasurementFrameAndRotationMatrixFromGradients(newDwi); 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(); GradientDirectionContainerType::Pointer gradientContainer = static_cast ( newDwi->GetProperty(PropHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(); 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); newDwi->GetPropertyList()->ReplaceProperty( PropHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str(), GradProp::New( gradientContainer ) ); PropHelper propertyHelper( newDwi ); propertyHelper.InitializeImage(); 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(); GradientDirectionContainerType::Pointer gradientContainer = static_cast ( newDwi->GetProperty(PropHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(); for (unsigned int j=0; jSize(); j++) { if (gradientContainer->at(j)[0]<0) { gradientContainer->at(j) = -gradientContainer->at(j); } } PropHelper::CopyProperties(image, newDwi, true); newDwi->GetPropertyList()->ReplaceProperty( PropHelper::ORIGINALGRADIENTCONTAINERPROPERTYNAME.c_str(), GradProp::New( gradientContainer ) ); PropHelper propertyHelper( newDwi ); propertyHelper.InitializeImage(); 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 = static_cast (image->GetProperty(PropHelper::BVALUEMAPPROPERTYNAME.c_str()).GetPointer() ) ->GetBValueMap(); GradientDirectionContainerType::Pointer gradientContainer = static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(); 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 = static_cast (image->GetProperty(PropHelper::BVALUEMAPPROPERTYNAME.c_str()).GetPointer() ) ->GetBValueMap(); 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; } GradientDirectionContainerType::Pointer gradientContainer = static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer(); 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->Update(); mitk::Image::Pointer newImage = mitk::GrabItkImageMemory( filter->GetOutput() ); PropHelper::CopyProperties(image, newImage, true); newImage->GetPropertyList()->ReplaceProperty( PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str(), GradProp::New( filter->GetGradientDirections() ) ); PropHelper propertyHelper( newImage ); propertyHelper.InitializeImage(); 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); } 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( static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer() ); bValueContainer.push_back( static_cast (image->GetProperty(PropHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue() ); name += node->GetName().c_str(); } { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(image2, itkVectorImagePointer); imageContainer.push_back( itkVectorImagePointer ); gradientListContainer.push_back( static_cast ( image2->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer() ); bValueContainer.push_back( static_cast (image2->GetProperty(PropHelper::REFERENCEBVALUEPROPERTYNAME.c_str()).GetPointer() ) ->GetValue() ); 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() ); newImage->GetPropertyList()->ReplaceProperty( PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str(), GradProp::New( filter->GetOutputGradients() ) ); newImage->GetPropertyList()->ReplaceProperty( PropHelper::MEASUREMENTFRAMEPROPERTYNAME.c_str(), mitk::MeasurementFrameProperty::New( mf ) ); newImage->GetPropertyList()->ReplaceProperty( PropHelper::REFERENCEBVALUEPROPERTYNAME.c_str(), mitk::FloatProperty::New( filter->GetB_Value() ) ); PropHelper propertyHelper( newImage ); propertyHelper.InitializeImage(); 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( static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer() ); 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( static_cast ( image->GetProperty(PropHelper::GRADIENTCONTAINERPROPERTYNAME.c_str()).GetPointer() ) ->GetGradientDirectionsContainer() ); 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 propertyHelper(newDwi); propertyHelper.AverageRedundantGradients(m_Controls->m_Blur->value()); propertyHelper.InitializeImage(); 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/CMakeLists.txt b/Plugins/org.mitk.gui.qt.diffusionimaging.python/CMakeLists.txt index a0feafa1d4..67809aa513 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.python/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.python/CMakeLists.txt @@ -1,14 +1,14 @@ if(MITK_USE_Python) # The project name must correspond to the directory name of your plug-in # and must not contain periods. project(org_mitk_gui_qt_diffusionimaging_python) mitk_create_plugin( EXPORT_DIRECTIVE DIFFUSIONIMAGING_PREPROCESSING_EXPORT EXPORTED_INCLUDE_SUFFIXES src MODULE_DEPENDS MitkDiffusionCore MitkPython MitkQtPython - PACKAGE_DEPENDS Qt5|Network + PACKAGE_DEPENDS Qt5|Network BetData ) endif() diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.cpp index a0f5da805b..e527f6dcc7 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.cpp @@ -1,229 +1,218 @@ /*=================================================================== 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. ===================================================================*/ //misc #define _USE_MATH_DEFINES #include // Blueberry #include #include // Qmitk #include "QmitkBrainExtractionView.h" // MITK #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#define _USE_MATH_DEFINES -#include - - -FileDownloader::FileDownloader(QObject *parent) : QObject(parent) -{ - -} - -void FileDownloader::download(QUrl url) -{ - connect(&m_WebCtrl, SIGNAL (finished(QNetworkReply*)), SLOT (Downloaded(QNetworkReply*))); - - QNetworkRequest request(url); - m_WebCtrl.get(request); -} - -void FileDownloader::Downloaded(QNetworkReply *reply) -{ - MITK_INFO << "FileDownloader::Downloaded TESTTEST"; - QFile localFile("/home/neher/test_download.tar.gz"); - if (!localFile.open(QIODevice::WriteOnly)) - return; - localFile.write(reply->readAll()); - localFile.close(); - delete reply; +#include +#include +#include +#include +#include - QMessageBox::information(nullptr, "FILE DOWNLOADED", "BLABL"); -} +#include -FileDownloader::~FileDownloader() -{ +#define _USE_MATH_DEFINES +#include -} +typedef itksys::SystemTools ist; const std::string QmitkBrainExtractionView::VIEW_ID = "org.mitk.views.brainextraction"; QmitkBrainExtractionView::QmitkBrainExtractionView() : QmitkAbstractView() , m_Controls( 0 ) , m_DiffusionImage( nullptr ) { } // Destructor QmitkBrainExtractionView::~QmitkBrainExtractionView() { } void QmitkBrainExtractionView::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::QmitkBrainExtractionViewControls; m_Controls->setupUi( parent ); connect( m_Controls->m_ImageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(UpdateGUI()) ); connect( m_Controls->m_StartButton, SIGNAL(clicked()), this, SLOT(StartBrainExtraction()) ); this->m_Parent = parent; m_Controls->m_ImageBox->SetDataStorage(this->GetDataStorage()); mitk::NodePredicateDimension::Pointer dimPred = mitk::NodePredicateDimension::New(3); mitk::TNodePredicateDataType::Pointer isImagePredicate = mitk::TNodePredicateDataType::New(); -// m_Controls->m_ImageBox->SetPredicate(mitk::NodePredicateAnd::New(isImagePredicate,dimPred)); m_Controls->m_ImageBox->SetPredicate(isImagePredicate); UpdateGUI(); - - std::string module_library_full_path = us::GetModuleContext()->GetModule()->GetLocation(); - std::string library_file; - itksys::SystemTools::SplitProgramPath(module_library_full_path, m_ModulePath, library_file); - m_ModulePath += "/BET"; } } void QmitkBrainExtractionView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& ) { } void QmitkBrainExtractionView::UpdateGUI() { if (m_Controls->m_ImageBox->GetSelectedNode().IsNotNull()) m_Controls->m_StartButton->setEnabled(true); else m_Controls->m_StartButton->setEnabled(false); } void QmitkBrainExtractionView::SetFocus() { UpdateGUI(); m_Controls->m_StartButton->setFocus(); } -void QmitkBrainExtractionView::StartBrainExtraction() +std::string QmitkBrainExtractionView::GetPythonFile(std::string filename) { -// FileDownloader dl; -// dl.download(QUrl("http://mitk.org/download/releases/MITK-2012.06/MITK-2012.06.0-src.tar.gz")); + std::string out = ""; + std::string bet_dir = std::string(BetData_DIR); + + if ( ist::FileExists(bet_dir + filename) ) + out = bet_dir + filename; + else if ( ist::FileExists( ist::GetCurrentWorkingDirectory() + filename) ) + out = ist::GetCurrentWorkingDirectory() + filename; + else if ( ist::FileExists( ist::GetCurrentWorkingDirectory() + "BetData/" + filename) ) + out = ist::GetCurrentWorkingDirectory() + "BetData/" + filename; + + return out; +} + +void QmitkBrainExtractionView::StartBrainExtraction() +{ mitk::DataNode::Pointer node = m_Controls->m_ImageBox->GetSelectedNode(); mitk::Image::Pointer mitk_image = dynamic_cast(node->GetData()); bool missing_file = false; std::string missing_file_string = ""; - if ( !itksys::SystemTools::FileExists(m_ModulePath + "/brain_extraction_script.py") ) + if ( GetPythonFile("brain_extraction_script.py").empty() ) { - missing_file_string += "Brain extraction script file missing:\n" + m_ModulePath + "/brain_extraction_script.py\n\n"; + missing_file_string += "Brain extraction script file missing: brain_extraction_script.py\n\n"; missing_file = true; } - if ( !itksys::SystemTools::FileExists(m_ModulePath + "/brain_extraction_model.model") ) + if ( GetPythonFile("brain_extraction_model.model").empty() ) { - missing_file_string += "Brain extraction model file missing:\n" + m_ModulePath + "/brain_extraction_model.model\n\n"; + missing_file_string += "Brain extraction model file missing: brain_extraction_model.model\n\n"; missing_file = true; } - if ( !itksys::SystemTools::FileExists(m_ModulePath + "/basic_config_just_like_braintumor.py") ) + if ( GetPythonFile("basic_config_just_like_braintumor.py").empty() ) { - missing_file_string += "Config file missing:\n" + m_ModulePath + "/basic_config_just_like_braintumor.py\n\n"; + missing_file_string += "Config file missing: basic_config_just_like_braintumor.py\n\n"; missing_file = true; } if (missing_file) { QMessageBox::warning(nullptr, "Error", (missing_file_string).c_str(), QMessageBox::Ok); return; } try { us::ModuleContext* context = us::GetModuleContext(); us::ServiceReference m_PythonServiceRef = context->GetServiceReference(); mitk::IPythonService* m_PythonService = dynamic_cast ( context->GetService(m_PythonServiceRef) ); mitk::IPythonService::ForceLoadModule(); m_PythonService->Execute("import SimpleITK as sitk"); m_PythonService->Execute("import SimpleITK._SimpleITK as _SimpleITK"); m_PythonService->Execute("import numpy"); m_PythonService->CopyToPythonAsSimpleItkImage( mitk_image, "in_image"); - m_PythonService->Execute("model_file=\""+m_ModulePath+"/brain_extraction_model.model\""); - m_PythonService->Execute("config_file=\""+m_ModulePath+"/basic_config_just_like_braintumor.py\""); - m_PythonService->ExecuteScript(m_ModulePath + "/brain_extraction_script.py"); + m_PythonService->Execute("model_file=\""+GetPythonFile("brain_extraction_model.model")+"\""); + m_PythonService->Execute("config_file=\""+GetPythonFile("basic_config_just_like_braintumor.py")+"\""); + m_PythonService->ExecuteScript( GetPythonFile("brain_extraction_script.py") ); { mitk::Image::Pointer image = m_PythonService->CopySimpleItkImageFromPython("brain_mask"); mitk::DataNode::Pointer corrected_node = mitk::DataNode::New(); corrected_node->SetData( image ); QString name(node->GetName().c_str()); name += "_BrainMask"; corrected_node->SetName(name.toStdString()); GetDataStorage()->Add(corrected_node, node); } { mitk::Image::Pointer image = m_PythonService->CopySimpleItkImageFromPython("brain_extracted"); if(mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(mitk_image)) { mitk::DiffusionPropertyHelper propertyHelper(image); mitk::DiffusionPropertyHelper::CopyProperties(mitk_image, image, true); propertyHelper.InitializeImage(); } mitk::DataNode::Pointer corrected_node = mitk::DataNode::New(); corrected_node->SetData( image ); QString name(node->GetName().c_str()); name += "_SkullStripped"; corrected_node->SetName(name.toStdString()); GetDataStorage()->Add(corrected_node, node); + + mitk::RenderingManager::GetInstance()->InitializeViews( corrected_node->GetData()->GetTimeGeometry(), + mitk::RenderingManager::REQUEST_UPDATE_ALL, + true); } } catch(...) { QMessageBox::warning(nullptr, "Error", "File could not be processed.\nIs pytorch installed on your system?\nDoes your script use the correct input and output variable names (in: in_image & model, out: brain_mask & brain_extracted)?", QMessageBox::Ok); } } diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.h b/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.h index 6ffc91add7..a60a588042 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.h +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.python/src/internal/QmitkBrainExtractionView.h @@ -1,104 +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 #include #include #include "ui_QmitkBrainExtractionViewControls.h" #include #include #include -#include -#include -#include -#include #include -class QmitkBrainExtractionView; - -class FileDownloader : public QObject -{ - Q_OBJECT -public: - explicit FileDownloader(QObject *parent = 0); - - virtual ~FileDownloader(); - - QByteArray downloadedData() const; - void download(QUrl url); - -protected slots: - - void Downloaded(QNetworkReply* reply); - -private: - - QNetworkAccessManager m_WebCtrl; - -}; /*! \brief View for diffusion image registration / head motion correction */ class QmitkBrainExtractionView : 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::VectorImage< short, 3 > ItkDwiType; typedef mitk::GradientDirectionsProperty GradProp; QmitkBrainExtractionView(); virtual ~QmitkBrainExtractionView(); virtual void CreateQtPartControl(QWidget *parent) override; void SetFocus() override; protected slots: void StartBrainExtraction(); void UpdateGUI(); ///< update button activity etc. dpending on current datamanager selection + std::string GetPythonFile(std::string filename); + protected: /// \brief called by QmitkAbstractView when DataManager's selection has changed virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList& nodes) override; Ui::QmitkBrainExtractionViewControls* m_Controls; mitk::Image::Pointer m_DiffusionImage; std::vector< mitk::DataNode::Pointer > m_SelectedDiffusionNodes; private: void UpdateRegistrationStatus(); ///< update textual status display of the Registration process // the Qt parent of our GUI (NOT of this object) QWidget* m_Parent; - std::string m_ModulePath; }; diff --git a/Plugins/org.mitk.gui.qt.diffusionimaging.registration/src/internal/QmitkSimpleRegistrationView.cpp b/Plugins/org.mitk.gui.qt.diffusionimaging.registration/src/internal/QmitkSimpleRegistrationView.cpp index c13e1f5199..7905469296 100644 --- a/Plugins/org.mitk.gui.qt.diffusionimaging.registration/src/internal/QmitkSimpleRegistrationView.cpp +++ b/Plugins/org.mitk.gui.qt.diffusionimaging.registration/src/internal/QmitkSimpleRegistrationView.cpp @@ -1,388 +1,392 @@ /*=================================================================== 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. ===================================================================*/ //misc #define _USE_MATH_DEFINES #include // Blueberry #include #include // Qmitk #include "QmitkSimpleRegistrationView.h" // MITK #include #include #include #include #include #include #include #include #include #include #include // Qt #include #define _USE_MATH_DEFINES #include const std::string QmitkSimpleRegistrationView::VIEW_ID = "org.mitk.views.simpleregistrationview"; QmitkSimpleRegistrationView::QmitkSimpleRegistrationView() : QmitkAbstractView() , m_Controls( 0 ) , m_RegistrationType(0) { } // Destructor QmitkSimpleRegistrationView::~QmitkSimpleRegistrationView() { } void QmitkSimpleRegistrationView::StartRegistration() { QmitkRegistrationJob* pJob; if (m_Controls->m_RegBox->currentIndex()==0) { mitk::MultiModalRigidDefaultRegistrationAlgorithm< ItkFloatImageType >::Pointer algo = mitk::MultiModalRigidDefaultRegistrationAlgorithm< ItkFloatImageType >::New(); pJob = new QmitkRegistrationJob(algo); m_RegistrationType = 0; } else { mitk::MultiModalAffineDefaultRegistrationAlgorithm< ItkFloatImageType >::Pointer algo = mitk::MultiModalAffineDefaultRegistrationAlgorithm< ItkFloatImageType >::New(); pJob = new QmitkRegistrationJob(algo); m_RegistrationType = 1; } pJob->setAutoDelete(true); m_MovingImageNode = m_Controls->m_MovingImageBox->GetSelectedNode(); mitk::Image::Pointer movingImage = dynamic_cast(m_MovingImageNode->GetData()); if (mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(movingImage)) { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(movingImage, itkVectorImagePointer); itk::ExtractDwiChannelFilter< short >::Pointer filter = itk::ExtractDwiChannelFilter< short >::New(); filter->SetInput( itkVectorImagePointer); filter->SetChannelIndex(m_Controls->m_MovingChannelBox->value()); filter->Update(); mitk::Image::Pointer newImage = mitk::Image::New(); newImage->InitializeByItk( filter->GetOutput() ); newImage->SetImportChannel( filter->GetOutput()->GetBufferPointer() ); pJob->m_spMovingData = newImage; } else pJob->m_spMovingData = movingImage; mitk::Image::Pointer fixedImage = dynamic_cast(m_Controls->m_FixedImageBox->GetSelectedNode()->GetData()); if (mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(fixedImage)) { ItkDwiType::Pointer itkVectorImagePointer = ItkDwiType::New(); mitk::CastToItkImage(fixedImage, itkVectorImagePointer); itk::ExtractDwiChannelFilter< short >::Pointer filter = itk::ExtractDwiChannelFilter< short >::New(); filter->SetInput( itkVectorImagePointer); filter->SetChannelIndex(m_Controls->m_MovingChannelBox->value()); filter->Update(); mitk::Image::Pointer newImage = mitk::Image::New(); newImage->InitializeByItk( filter->GetOutput() ); newImage->SetImportChannel( filter->GetOutput()->GetBufferPointer() ); pJob->m_spTargetData = newImage; } else pJob->m_spTargetData = fixedImage; pJob->m_TargetDataUID = mitk::EnsureUID(m_Controls->m_FixedImageBox->GetSelectedNode()->GetData()); pJob->m_MovingDataUID = mitk::EnsureUID(m_Controls->m_MovingImageBox->GetSelectedNode()->GetData()); connect(pJob, SIGNAL(RegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer, const QmitkRegistrationJob*)), this, SLOT(OnRegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer, const QmitkRegistrationJob*)), Qt::BlockingQueuedConnection); QThreadPool* threadPool = QThreadPool::globalInstance(); threadPool->start(pJob); m_Controls->m_RegistrationStartButton->setEnabled(false); m_Controls->m_RegistrationStartButton->setText("Registration in progress ..."); } void QmitkSimpleRegistrationView::OnRegResultIsAvailable(mitk::MAPRegistrationWrapper::Pointer spResultRegistration, const QmitkRegistrationJob* job) { mitk::Image::Pointer movingImage = dynamic_cast(m_MovingImageNode->GetData()); mitk::Image::Pointer image; if (m_RegistrationType==0) { image = mitk::ImageMappingHelper::refineGeometry(movingImage, spResultRegistration, true); } else { if (!mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(movingImage)) { image = mitk::ImageMappingHelper::map(movingImage, spResultRegistration, false, 0, job->m_spTargetData->GetGeometry(), false, 0, mitk::ImageMappingInterpolator::BSpline_3); } else { typedef itk::ComposeImageFilter < ITKDiffusionVolumeType > ComposeFilterType; ComposeFilterType::Pointer composer = ComposeFilterType::New(); ItkDwiType::Pointer itkVectorImagePointer = mitk::DiffusionPropertyHelper::GetItkVectorImage(movingImage); for (unsigned int i=0; iGetVectorLength(); ++i) { itk::ExtractDwiChannelFilter< short >::Pointer filter = itk::ExtractDwiChannelFilter< short >::New(); filter->SetInput( itkVectorImagePointer); filter->SetChannelIndex(i); filter->Update(); mitk::Image::Pointer gradientVolume = mitk::Image::New(); gradientVolume->InitializeByItk( filter->GetOutput() ); gradientVolume->SetImportChannel( filter->GetOutput()->GetBufferPointer() ); mitk::Image::Pointer registered_mitk_image = mitk::ImageMappingHelper::map(gradientVolume, spResultRegistration, false, 0, job->m_spTargetData->GetGeometry(), false, 0, mitk::ImageMappingInterpolator::BSpline_3); ITKDiffusionVolumeType::Pointer registered_itk_image = ITKDiffusionVolumeType::New(); mitk::CastToItkImage(registered_mitk_image, registered_itk_image); composer->SetInput(i, registered_itk_image); } composer->Update(); image = mitk::GrabItkImageMemory( composer->GetOutput() ); mitk::DiffusionPropertyHelper::CopyProperties(movingImage, image, true); } } if (mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(image)) { mitk::DiffusionPropertyHelper propertyHelper( image ); propertyHelper.InitializeImage(); } mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); resultNode->SetData(image); if (m_MovingImageNode.IsNotNull()) { m_MovingImageNode->SetVisibility(false); QString name = m_MovingImageNode->GetName().c_str(); if (m_RegistrationType==0) resultNode->SetName((name+"_registered (rigid)").toStdString().c_str()); else resultNode->SetName((name+"_registered (affine)").toStdString().c_str()); } else { if (m_RegistrationType==0) resultNode->SetName("Registered (rigid)"); else resultNode->SetName("Registered (affine)"); } resultNode->SetOpacity(0.6); resultNode->SetColor(0.0, 0.0, 1.0); GetDataStorage()->Add(resultNode); + mitk::RenderingManager::GetInstance()->InitializeViews( resultNode->GetData()->GetTimeGeometry(), + mitk::RenderingManager::REQUEST_UPDATE_ALL, + true); + if (m_Controls->m_RegOutputBox->isChecked()) { mitk::DataNode::Pointer registration_node = mitk::DataNode::New(); registration_node->SetData(spResultRegistration); if (m_RegistrationType==0) registration_node->SetName("Registration Object (rigid)"); else registration_node->SetName("Registration Object (affine)"); GetDataStorage()->Add(registration_node, resultNode); } this->GetRenderWindowPart()->RequestUpdate(); m_Controls->m_RegistrationStartButton->setEnabled(true); m_Controls->m_RegistrationStartButton->setText("Start Registration"); m_MovingImageNode = nullptr; TractoChanged(); } void QmitkSimpleRegistrationView::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::QmitkSimpleRegistrationViewControls; m_Controls->setupUi( parent ); m_Controls->m_FixedImageBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_MovingImageBox->SetDataStorage(this->GetDataStorage()); mitk::TNodePredicateDataType::Pointer isImagePredicate = mitk::TNodePredicateDataType::New(); m_Controls->m_FixedImageBox->SetPredicate(isImagePredicate); m_Controls->m_MovingImageBox->SetPredicate(isImagePredicate); mitk::TNodePredicateDataType::Pointer isFib = mitk::TNodePredicateDataType::New(); mitk::TNodePredicateDataType::Pointer isReg = mitk::TNodePredicateDataType::New(); m_Controls->m_TractoBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_RegObjectBox->SetDataStorage(this->GetDataStorage()); m_Controls->m_TractoBox->SetPredicate(isFib); m_Controls->m_RegObjectBox->SetPredicate(isReg); connect( m_Controls->m_FixedImageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(FixedImageChanged()) ); connect( m_Controls->m_MovingImageBox, SIGNAL(currentIndexChanged(int)), this, SLOT(MovingImageChanged()) ); connect( m_Controls->m_TractoBox, SIGNAL(currentIndexChanged(int)), this, SLOT(TractoChanged()) ); connect( m_Controls->m_RegObjectBox, SIGNAL(currentIndexChanged(int)), this, SLOT(TractoChanged()) ); connect( m_Controls->m_RegistrationStartButton, SIGNAL(clicked()), this, SLOT(StartRegistration()) ); connect( m_Controls->m_TractoRegistrationStartButton, SIGNAL(clicked()), this, SLOT(StartTractoRegistration()) ); FixedImageChanged(); MovingImageChanged(); TractoChanged(); } } void QmitkSimpleRegistrationView::StartTractoRegistration() { mitk::FiberBundle::Pointer fib = dynamic_cast(m_Controls->m_TractoBox->GetSelectedNode()->GetData()); mitk::MAPRegistrationWrapper::Pointer reg = dynamic_cast(m_Controls->m_RegObjectBox->GetSelectedNode()->GetData()); mitk::MITKRegistrationHelper::Affine3DTransformType::Pointer affine = mitk::MITKRegistrationHelper::getAffineMatrix(reg, false); mitk::FiberBundle::Pointer fib_copy = fib->GetDeepCopy(); fib_copy->TransformFibers(affine); mitk::DataNode::Pointer registration_node = mitk::DataNode::New(); registration_node->SetData(fib_copy); QString name = m_Controls->m_TractoBox->GetSelectedNode()->GetName().c_str(); registration_node->SetName((name+"_registered").toStdString().c_str()); GetDataStorage()->Add(registration_node, m_Controls->m_TractoBox->GetSelectedNode()); } void QmitkSimpleRegistrationView::TractoChanged() { if (m_Controls->m_RegObjectBox->GetSelectedNode().IsNotNull() && m_Controls->m_TractoBox->GetSelectedNode().IsNotNull()) m_Controls->m_TractoRegistrationStartButton->setEnabled(true); else m_Controls->m_TractoRegistrationStartButton->setEnabled(false); } void QmitkSimpleRegistrationView::FixedImageChanged() { if (m_Controls->m_FixedImageBox->GetSelectedNode().IsNotNull()) { mitk::Image::Pointer image = dynamic_cast(m_Controls->m_FixedImageBox->GetSelectedNode()->GetData()); int channels = image->GetNumberOfChannels(); int dims = image->GetDimension(); int fourth_dim_size = image->GetTimeSteps(); bool isdiff = mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(image); if (dims==4 || channels>1) { m_Controls->m_FixedChannelBox->setEnabled(false); m_Controls->m_RegistrationStartButton->setEnabled(false); } if (isdiff) { m_Controls->m_FixedChannelBox->setEnabled(true); if (fourth_dim_size>1) m_Controls->m_FixedChannelBox->setMaximum(fourth_dim_size-1); else if (isdiff) m_Controls->m_FixedChannelBox->setMaximum(mitk::DiffusionPropertyHelper::GetGradientContainer(image)->Size()-1); } else { m_Controls->m_FixedChannelBox->setEnabled(false); } m_Controls->m_RegistrationStartButton->setEnabled(true); } else { m_Controls->m_FixedChannelBox->setEnabled(false); m_Controls->m_RegistrationStartButton->setEnabled(false); } } void QmitkSimpleRegistrationView::MovingImageChanged() { if (m_Controls->m_MovingImageBox->GetSelectedNode().IsNotNull()) { mitk::Image::Pointer image = dynamic_cast(m_Controls->m_MovingImageBox->GetSelectedNode()->GetData()); int channels = image->GetNumberOfChannels(); int dims = image->GetDimension(); int fourth_dim_size = image->GetTimeSteps(); bool isdiff = mitk::DiffusionPropertyHelper::IsDiffusionWeightedImage(image); if (dims==4 || channels>1) { m_Controls->m_MovingChannelBox->setEnabled(false); m_Controls->m_RegistrationStartButton->setEnabled(false); } if (isdiff) { m_Controls->m_MovingChannelBox->setEnabled(true); if (fourth_dim_size>1) m_Controls->m_MovingChannelBox->setMaximum(fourth_dim_size-1); else if (isdiff) m_Controls->m_MovingChannelBox->setMaximum(mitk::DiffusionPropertyHelper::GetGradientContainer(image)->Size()-1); } else { m_Controls->m_MovingChannelBox->setEnabled(false); } m_Controls->m_RegistrationStartButton->setEnabled(true); } else { m_Controls->m_MovingChannelBox->setEnabled(false); m_Controls->m_RegistrationStartButton->setEnabled(false); } } void QmitkSimpleRegistrationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& ) { FixedImageChanged(); MovingImageChanged(); TractoChanged(); } void QmitkSimpleRegistrationView::SetFocus() { m_Controls->m_RegistrationStartButton->setFocus(); FixedImageChanged(); MovingImageChanged(); } diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.cpp index 18d2d6c4d1..5e2a6fa437 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.cpp @@ -1,241 +1,238 @@ /*=================================================================== 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 "QmitkImageStatisticsCalculationThread.h" //QT headers #include #include #include #include #include QmitkImageStatisticsCalculationThread::QmitkImageStatisticsCalculationThread() : QThread() , m_StatisticsImage(nullptr) , m_BinaryMask(nullptr) , m_PlanarFigureMask(nullptr) , m_TimeStep(0) , m_IgnoreZeros(false) - , m_HistogramBinSize(10.0) + , m_HistogramNBins(100) , m_StatisticChanged(false) , m_CalculationSuccessful(false) { } QmitkImageStatisticsCalculationThread::~QmitkImageStatisticsCalculationThread() { } void QmitkImageStatisticsCalculationThread::Initialize( mitk::Image::Pointer image, mitk::Image::Pointer binaryImage, mitk::PlanarFigure::Pointer planarFig ) { // reset old values if( this->m_StatisticsImage.IsNotNull() ) this->m_StatisticsImage = nullptr; if( this->m_BinaryMask.IsNotNull() ) this->m_BinaryMask = nullptr; if( this->m_PlanarFigureMask.IsNotNull()) this->m_PlanarFigureMask = nullptr; // set new values if passed in if(image.IsNotNull()) this->m_StatisticsImage = image->Clone(); if(binaryImage.IsNotNull()) this->m_BinaryMask = binaryImage->Clone(); if(planarFig.IsNotNull()) this->m_PlanarFigureMask = planarFig->Clone(); } void QmitkImageStatisticsCalculationThread::SetTimeStep( int times ) { this->m_TimeStep = times; } int QmitkImageStatisticsCalculationThread::GetTimeStep() { return this->m_TimeStep; } std::vector QmitkImageStatisticsCalculationThread::GetStatisticsData() { return this->m_StatisticsVector; } mitk::Image::Pointer QmitkImageStatisticsCalculationThread::GetStatisticsImage() { return this->m_StatisticsImage; } void QmitkImageStatisticsCalculationThread::SetIgnoreZeroValueVoxel(bool _arg) { this->m_IgnoreZeros = _arg; } bool QmitkImageStatisticsCalculationThread::GetIgnoreZeroValueVoxel() { return this->m_IgnoreZeros; } -void QmitkImageStatisticsCalculationThread::SetHistogramBinSize(double size) +void QmitkImageStatisticsCalculationThread::SetHistogramNBins(unsigned int nbins) { - this->m_HistogramBinSize = size; + this->m_HistogramNBins = nbins; } -double QmitkImageStatisticsCalculationThread::GetHistogramBinSize() const +unsigned int QmitkImageStatisticsCalculationThread::GetHistogramNBins() const { - return this->m_HistogramBinSize; + return this->m_HistogramNBins; } std::string QmitkImageStatisticsCalculationThread::GetLastErrorMessage() { return m_message; } QmitkImageStatisticsCalculationThread::HistogramType::Pointer QmitkImageStatisticsCalculationThread::GetTimeStepHistogram(unsigned int t) { if (t >= this->m_HistogramVector.size()) return nullptr; return this->m_HistogramVector[t]; } bool QmitkImageStatisticsCalculationThread::GetStatisticsChangedFlag() { return m_StatisticChanged; } bool QmitkImageStatisticsCalculationThread::GetStatisticsUpdateSuccessFlag() { return m_CalculationSuccessful; } void QmitkImageStatisticsCalculationThread::run() { bool statisticCalculationSuccessful = true; mitk::ImageStatisticsCalculator::Pointer calculator = mitk::ImageStatisticsCalculator::New(); if(this->m_StatisticsImage.IsNotNull()) { calculator->SetInputImage(m_StatisticsImage); } else { statisticCalculationSuccessful = false; } // Bug 13416 : The ImageStatistics::SetImageMask() method can throw exceptions, i.e. when the dimensionality // of the masked and input image differ, we need to catch them and mark the calculation as failed // the same holds for the ::SetPlanarFigure() try { if(this->m_BinaryMask.IsNotNull()) { mitk::ImageMaskGenerator::Pointer imgMask = mitk::ImageMaskGenerator::New(); imgMask->SetImageMask(m_BinaryMask); calculator->SetMask(imgMask.GetPointer()); } if(this->m_PlanarFigureMask.IsNotNull()) { mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New(); pfMaskGen->SetInputImage(m_StatisticsImage); pfMaskGen->SetPlanarFigure(m_PlanarFigureMask); calculator->SetMask(pfMaskGen.GetPointer()); } } catch (const mitk::Exception& e) { MITK_ERROR << "MITK Exception: " << e.what(); statisticCalculationSuccessful = false; } catch( const itk::ExceptionObject& e) { MITK_ERROR << "ITK Exception:" << e.what(); statisticCalculationSuccessful = false; } catch ( const std::runtime_error &e ) { MITK_ERROR<< "Runtime Exception: " << e.what(); statisticCalculationSuccessful = false; } catch ( const std::exception &e ) { MITK_ERROR<< "Standard Exception: " << e.what(); statisticCalculationSuccessful = false; } bool statisticChanged = false; if (this->m_IgnoreZeros) { mitk::IgnorePixelMaskGenerator::Pointer ignorePixelValueMaskGen = mitk::IgnorePixelMaskGenerator::New(); ignorePixelValueMaskGen->SetIgnoredPixelValue(0); ignorePixelValueMaskGen->SetInputImage(m_StatisticsImage); calculator->SetSecondaryMask(ignorePixelValueMaskGen.GetPointer()); } else { calculator->SetSecondaryMask(nullptr); } - calculator->SetBinSizeForHistogramStatistics(m_HistogramBinSize); - - //calculator->SetHistogramBinSize( m_HistogramBinSize ); - //calculator->SetUseDefaultBinSize( m_UseDefaultBinSize ); + calculator->SetNBinsForHistogramStatistics(m_HistogramNBins); for (unsigned int i = 0; i < m_StatisticsImage->GetTimeSteps(); i++) { try { calculator->GetStatistics(i); } catch ( mitk::Exception& e) { //m_message = e.GetDescription(); MITK_ERROR<< "MITK Exception: " << e.what(); statisticCalculationSuccessful = false; } catch ( const std::runtime_error &e ) { //m_message = "Failure: " + std::string(e.what()); MITK_ERROR<< "Runtime Exception: " << e.what(); statisticCalculationSuccessful = false; } catch ( const std::exception &e ) { //m_message = "Failure: " + std::string(e.what()); MITK_ERROR<< "Standard Exception: " << e.what(); statisticCalculationSuccessful = false; } } this->m_StatisticChanged = statisticChanged; this->m_CalculationSuccessful = statisticCalculationSuccessful; if(statisticCalculationSuccessful) { this->m_StatisticsVector.clear(); this->m_HistogramVector.clear(); for (unsigned int i = 0; i < m_StatisticsImage->GetTimeSteps(); i++) { this->m_StatisticsVector.push_back(calculator->GetStatistics(i)); this->m_HistogramVector.push_back((HistogramType*)this->m_StatisticsVector[i]->GetHistogram()); } } } diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.h b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.h index 2e9ade1974..0f547ce224 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.h +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.h @@ -1,112 +1,112 @@ /*=================================================================== 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 QMITKIMAGESTATISTICSCALCULATIONTHREAD_H_INCLUDED #define QMITKIMAGESTATISTICSCALCULATIONTHREAD_H_INCLUDED //QT headers #include #include //mitk headers #include "mitkImage.h" #include "mitkPlanarFigure.h" #include "mitkImageStatisticsCalculator.h" // itk headers #ifndef __itkHistogram_h #include #endif /** /brief This class is executed as background thread for image statistics calculation. * Documentation: This class is derived from QThread and is intended to be used by QmitkImageStatisticsView to run the image statistics calculation in a background thread keepung the gui usable. * \ingroup Plugins/MeasurementToolbox */ class QmitkImageStatisticsCalculationThread : public QThread { Q_OBJECT public: typedef itk::Statistics::Histogram HistogramType; /*! /brief standard constructor. */ QmitkImageStatisticsCalculationThread(); /*! /brief standard destructor. */ ~QmitkImageStatisticsCalculationThread(); /*! /brief Initializes the object with necessary data. */ void Initialize( mitk::Image::Pointer image, mitk::Image::Pointer binaryImage, mitk::PlanarFigure::Pointer planarFig ); /*! /brief returns the calculated image statistics. */ std::vector GetStatisticsData(); /*! /brief */ mitk::Image::Pointer GetStatisticsImage(); /*! /brief Set the time step of the image you want to process. */ void SetTimeStep( int times ); /*! /brief Get the time step of the image you want to process. */ int GetTimeStep(); /*! /brief Set flag to ignore zero valued voxels */ void SetIgnoreZeroValueVoxel( bool _arg ); /*! /brief Get status of zero value voxel ignoring. */ bool GetIgnoreZeroValueVoxel(); /*! /brief Set bin size for histogram resolution.*/ - void SetHistogramBinSize( double size); + void SetHistogramNBins( unsigned int nbins); /*! /brief Get bin size for histogram resolution.*/ - double GetHistogramBinSize() const; + unsigned int GetHistogramNBins() const; /*! /brief Returns the histogram of the currently selected time step. */ HistogramType::Pointer GetTimeStepHistogram(unsigned int t = 0); /*! /brief Returns a flag indicating if the statistics have changed during calculation */ bool GetStatisticsChangedFlag(); /*! /brief Returns a flag the indicates if the statistics are updated successfully */ bool GetStatisticsUpdateSuccessFlag(); /*! /brief Method called once the thread is executed. */ void run() override; std::string GetLastErrorMessage(); private: //member declaration mitk::Image::Pointer m_StatisticsImage; ///< member variable holds the input image for which the statistics need to be calculated. mitk::Image::Pointer m_BinaryMask; ///< member variable holds the binary mask image for segmentation image statistics calculation. mitk::PlanarFigure::Pointer m_PlanarFigureMask; ///< member variable holds the planar figure for segmentation image statistics calculation. std::vector m_StatisticsVector; ///< member variable holds the result structs. int m_TimeStep; ///< member variable holds the time step for statistics calculation bool m_IgnoreZeros; ///< member variable holds flag to indicate if zero valued voxel should be suppressed - double m_HistogramBinSize; ///< member variable holds the bin size for histogram resolution. + unsigned int m_HistogramNBins; ///< member variable holds the bin size for histogram resolution. bool m_StatisticChanged; ///< flag set if statistics have changed bool m_CalculationSuccessful; ///< flag set if statistics calculation was successful std::vector m_HistogramVector; ///< member holds the histograms of all time steps. std::string m_message; }; #endif // QMITKIMAGESTATISTICSCALCULATIONTHREAD_H_INCLUDED diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp index 045a4bafcb..de1e68e5a5 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -1,1375 +1,1293 @@ /*=================================================================== 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 "QmitkImageStatisticsView.h" // Qt includes #include #include #include // berry includes #include // mitk includes -#include "mitkNodePredicateDataType.h" -#include "mitkNodePredicateOr.h" -#include "mitkPlanarFigureInteractor.h" -#include "mitkImageTimeSelector.h" +#include +#include +#include +#include #include #include +#include // itk includes #include "itksys/SystemTools.hxx" - -#include - #include "itkImageRegionConstIteratorWithIndex.h" #include //blueberry includes #include #include const std::string QmitkImageStatisticsView::VIEW_ID = "org.mitk.views.imagestatistics"; const int QmitkImageStatisticsView::STAT_TABLE_BASE_HEIGHT = 180; QmitkImageStatisticsView::QmitkImageStatisticsView(QObject* /*parent*/, const char* /*name*/) : m_Controls( nullptr ), - m_TimeStepperAdapter( nullptr ), m_SelectedImage( nullptr ), m_SelectedImageMask( nullptr ), m_SelectedPlanarFigure( nullptr ), m_ImageObserverTag( -1 ), m_ImageMaskObserverTag( -1 ), m_PlanarFigureObserverTag( -1 ), m_TimeObserverTag( -1 ), m_CurrentStatisticsValid( false ), m_StatisticsUpdatePending( false ), m_DataNodeSelectionChanged ( false ), m_Visible(false) { this->m_CalculationThread = new QmitkImageStatisticsCalculationThread; } QmitkImageStatisticsView::~QmitkImageStatisticsView() { if ( m_SelectedImage != nullptr ) m_SelectedImage->RemoveObserver( m_ImageObserverTag ); if ( m_SelectedImageMask != nullptr ) m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); if ( m_SelectedPlanarFigure != nullptr ) m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); while(this->m_CalculationThread->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } delete this->m_CalculationThread; } void QmitkImageStatisticsView::CreateQtPartControl(QWidget *parent) { if (m_Controls == nullptr) { m_Controls = new Ui::QmitkImageStatisticsViewControls; m_Controls->setupUi(parent); CreateConnections(); m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex(0); m_Controls->m_BinSizeFrame->setEnabled(false); } } void QmitkImageStatisticsView::OnPageSuccessfullyLoaded() { berry::IPreferencesService* prefService = berry::WorkbenchPlugin::GetDefault()->GetPreferencesService(); m_StylePref = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE); QString styleName = m_StylePref->Get(berry::QtPreferences::QT_STYLE_NAME, ""); if (styleName == ":/org.blueberry.ui.qt/darkstyle.qss") { this->m_Controls->m_JSHistogram->SetTheme(QmitkChartWidget::ChartStyle::darkstyle); } else { this->m_Controls->m_JSHistogram->SetTheme(QmitkChartWidget::ChartStyle::lightstyle); } } void QmitkImageStatisticsView::CreateConnections() { if ( m_Controls ) { connect( (QObject*)(this->m_Controls->m_ButtonCopyHistogramToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(OnClipboardHistogramButtonClicked()) ); connect( (QObject*)(this->m_Controls->m_ButtonCopyStatisticsToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(OnClipboardStatisticsButtonClicked()) ); connect( (QObject*)(this->m_Controls->m_IgnoreZerosCheckbox), SIGNAL(clicked()),(QObject*) this, SLOT(OnIgnoreZerosCheckboxClicked()) ); connect( (QObject*) this->m_CalculationThread, SIGNAL(finished()),this, SLOT( OnThreadedStatisticsCalculationEnds()),Qt::QueuedConnection); connect( (QObject*) this, SIGNAL(StatisticsUpdate()),this, SLOT( RequestStatisticsUpdate()), Qt::QueuedConnection); connect( (QObject*) this->m_Controls->m_StatisticsTable, SIGNAL(cellDoubleClicked(int,int)),this, SLOT( JumpToCoordinates(int,int)) ); connect((QObject*)(this->m_Controls->m_barRadioButton), SIGNAL(clicked()), (QObject*)(this), SLOT(OnBarRadioButtonSelected())); connect((QObject*)(this->m_Controls->m_lineRadioButton), SIGNAL(clicked()), (QObject*)(this), SLOT(OnLineRadioButtonSelected())); - connect( (QObject*) (this->m_Controls->m_HistogramBinSizeSpinbox), SIGNAL(editingFinished()), this, SLOT(OnHistogramBinSizeBoxValueChanged())); - connect((QObject*)(this->m_Controls->m_UseDefaultBinSizeBox), SIGNAL(clicked()), (QObject*) this, SLOT(OnDefaultBinSizeBoxChanged())); + connect( (QObject*) (this->m_Controls->m_HistogramNBinsSpinbox), SIGNAL(editingFinished()), this, SLOT(OnHistogramNBinsCheckBoxValueChanged())); + connect((QObject*)(this->m_Controls->m_UseDefaultNBinsCheckBox), SIGNAL(clicked()), (QObject*) this, SLOT(OnDefaultNBinsSpinBoxChanged())); connect((QObject*)(this->m_Controls->m_ShowSubchartCheckBox), SIGNAL(clicked()), (QObject*) this, SLOT(OnShowSubchartBoxChanged())); connect((QObject*)(this->m_Controls->m_JSHistogram), SIGNAL(PageSuccessfullyLoaded()), (QObject*) this, SLOT(OnPageSuccessfullyLoaded())); } } -void QmitkImageStatisticsView::OnDefaultBinSizeBoxChanged() +void QmitkImageStatisticsView::OnDefaultNBinsSpinBoxChanged() { + if (this->m_Controls->m_UseDefaultNBinsCheckBox->isChecked()) { + m_Controls->m_HistogramNBinsSpinbox->setValue(100); + this->m_CalculationThread->SetHistogramNBins(m_Controls->m_HistogramNBinsSpinbox->value()); + m_HistogramNBins = m_Controls->m_HistogramNBinsSpinbox->value(); + } + m_Controls->m_BinSizeFrame->setEnabled(!m_Controls->m_UseDefaultNBinsCheckBox->isChecked()); - m_Controls->m_BinSizeFrame->setEnabled(!m_Controls->m_UseDefaultBinSizeBox->isChecked()); - -if (m_CalculationThread != nullptr){ - m_Controls->m_HistogramBinSizeSpinbox->setValue(m_CalculationThread->GetHistogramBinSize()); -} -this->UpdateStatistics(); + this->UpdateStatistics(); } void QmitkImageStatisticsView::OnShowSubchartBoxChanged() { bool showSubchart = this->m_Controls->m_ShowSubchartCheckBox->isChecked(); this->m_Controls->m_JSHistogram->Reload(showSubchart); } void QmitkImageStatisticsView::OnBarRadioButtonSelected() { this->m_Controls->m_JSHistogram->SetChartTypeForAllDataAndReload(QmitkChartWidget::ChartType::bar); } void QmitkImageStatisticsView::OnLineRadioButtonSelected() { this->m_Controls->m_JSHistogram->SetChartTypeForAllDataAndReload(QmitkChartWidget::ChartType::line); } void QmitkImageStatisticsView::PartClosed(const berry::IWorkbenchPartReference::Pointer& ) { } void QmitkImageStatisticsView::OnTimeChanged(const itk::EventObject& e) { if (this->m_SelectedDataNodes.isEmpty() || this->m_SelectedImage == nullptr) return; const mitk::SliceNavigationController::GeometryTimeEvent* timeEvent = dynamic_cast(&e); assert(timeEvent != nullptr); int timestep = timeEvent->GetPos(); if (this->m_SelectedImage->GetTimeSteps() > 1) { for (int x = 0; x < this->m_Controls->m_StatisticsTable->columnCount(); x++) { for (int y = 0; y < this->m_Controls->m_StatisticsTable->rowCount(); y++) { QTableWidgetItem* item = this->m_Controls->m_StatisticsTable->item(y, x); if (item == nullptr) break; if (x == timestep) { item->setBackgroundColor(Qt::yellow); } else { if (y % 2 == 0) item->setBackground(this->m_Controls->m_StatisticsTable->palette().base()); else item->setBackground(this->m_Controls->m_StatisticsTable->palette().alternateBase()); } } } this->m_Controls->m_StatisticsTable->viewport()->update(); } if ((this->m_SelectedImage->GetTimeSteps() == 1 && timestep == 0) || this->m_SelectedImage->GetTimeSteps() > 1) { // display histogram for selected timestep this->m_Controls->m_JSHistogram->Clear(); QmitkImageStatisticsCalculationThread::HistogramType::ConstPointer histogram = (QmitkImageStatisticsCalculationThread::HistogramType::ConstPointer)this->m_CalculationThread->GetTimeStepHistogram(timestep); if (histogram.IsNotNull()) { bool closedFigure = this->m_CalculationThread->GetStatisticsUpdateSuccessFlag(); if (closedFigure) { + auto imageNameLabel = m_Controls->m_SelectedFeatureImageLabel->text().toStdString(); this->m_Controls->m_JSHistogram->AddData2D(ConvertHistogramToMap(histogram), imageNameLabel); if (this->m_Controls->m_lineRadioButton->isChecked()) { this->m_Controls->m_JSHistogram->SetChartType(imageNameLabel, QmitkChartWidget::ChartType::line); } else { this->m_Controls->m_JSHistogram->SetChartType(imageNameLabel, QmitkChartWidget::ChartType::bar); } this->m_Controls->m_JSHistogram->SetXAxisLabel("Grey value"); this->m_Controls->m_JSHistogram->SetYAxisLabel("Frequency"); this->m_Controls->m_JSHistogram->Show(this->m_Controls->m_ShowSubchartCheckBox->isChecked()); } } } } void QmitkImageStatisticsView::JumpToCoordinates(int row ,int col) { if(m_SelectedDataNodes.isEmpty()) { MITK_WARN("QmitkImageStatisticsView") << "No data node selected for statistics calculation." ; return; } mitk::Point3D world; if (row==5 && !m_WorldMinList.empty()) world = m_WorldMinList[col]; else if (row==4 && !m_WorldMaxList.empty()) world = m_WorldMaxList[col]; else return; mitk::IRenderWindowPart* part = this->GetRenderWindowPart(); if (part) { part->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SelectSliceByPoint(world); part->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()->SelectSliceByPoint(world); part->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()->SelectSliceByPoint(world); mitk::SliceNavigationController::GeometryTimeEvent timeEvent(this->m_SelectedImage->GetTimeGeometry(), col); part->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SetGeometryTime(timeEvent); } } void QmitkImageStatisticsView::OnIgnoreZerosCheckboxClicked() { emit StatisticsUpdate(); } void QmitkImageStatisticsView::OnClipboardHistogramButtonClicked() { if ( m_CurrentStatisticsValid && !( m_SelectedPlanarFigure != nullptr)) { const unsigned int t = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->GetPos(); typedef mitk::ImageStatisticsCalculator::HistogramType HistogramType; const HistogramType *histogram = this->m_CalculationThread->GetTimeStepHistogram(t).GetPointer(); QString clipboard( "Measurement \t Frequency\n" ); for ( HistogramType::ConstIterator it = histogram->Begin(); it != histogram->End(); ++it ) { - if( m_Controls->m_HistogramBinSizeSpinbox->value() == 1.0) - { - clipboard = clipboard.append( "%L1 \t %L2\n" ) - .arg( it.GetMeasurementVector()[0], 0, 'f', 0 ) - .arg( it.GetFrequency() ); - } - else - { - clipboard = clipboard.append( "%L1 \t %L2\n" ) - .arg( it.GetMeasurementVector()[0], 0, 'f', 2 ) - .arg( it.GetFrequency() ); - } + clipboard = clipboard.append( "%L1 \t %L2\n" ) + .arg( it.GetMeasurementVector()[0], 0, 'f', 2 ) + .arg( it.GetFrequency() ); } QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard ); } // If a (non-closed) PlanarFigure is selected, display a line profile widget else if ( m_CurrentStatisticsValid && (m_SelectedPlanarFigure != nullptr )) { /*auto intensity = m_Controls->m_JSHistogram->GetFrequency(); auto pixel = m_Controls->m_JSHistogram->GetMeasurement(); QString clipboard( "Pixel \t Intensity\n" ); auto j = pixel.begin(); for (auto i = intensity.begin(); i < intensity.end(); i++) { assert(j != pixel.end()); clipboard = clipboard.append( "%L1 \t %L2\n" ) .arg( (*j).toString()) .arg( (*i).toString()); j++; } QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard ); */ } else { QApplication::clipboard()->clear(); } } void QmitkImageStatisticsView::OnClipboardStatisticsButtonClicked() { QLocale tempLocal; QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); if ( m_CurrentStatisticsValid && !( m_SelectedPlanarFigure != nullptr)) { const std::vector &statistics = this->m_CalculationThread->GetStatisticsData(); // Set time borders for for loop ;) unsigned int startT, endT; if(this->m_Controls->m_CheckBox4dCompleteTable->checkState()==Qt::CheckState::Unchecked) { startT = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()-> GetPos(); endT = startT+1; } else { startT = 0; endT = statistics.size(); } QVector< QVector > statisticsTable; - QStringList headline; - - // Create Headline - headline << " " - << "Mean" - << "Median" - << "StdDev" - << "RMS" - << "Max" - << "Min" - << "NumberOfVoxels" - << "Skewness" - << "Kurtosis" - << "Uniformity" - << "Entropy" - << "MPP" - << "UPP" - << "V [mm³]"; + QStringList headline{ "Timestep", "Mean", "Median", "StdDev", "RMS", "Max", "Min", "NumberOfVoxels", "Skewness", "Kurtosis", "Uniformity", "Entropy", "MPP", "UPP", "V [mm³]" }; for(int i=0;i row; row.append(headline.at(i)); statisticsTable.append(row); } // Fill Table for(unsigned int t=startT;tGetMean()) << QString::number(statistics[t]->GetMedian()) << QString::number(statistics[t]->GetStd()) << QString::number(statistics[t]->GetRMS()) << QString::number(statistics[t]->GetMax()) << QString::number(statistics[t]->GetMin()) << QString::number(statistics[t]->GetN()) << QString::number(statistics[t]->GetSkewness()) << QString::number(statistics[t]->GetKurtosis()) << QString::number(statistics[t]->GetUniformity()) << QString::number(statistics[t]->GetEntropy()) << QString::number(statistics[t]->GetMPP()) << QString::number(statistics[t]->GetUPP()) << QString::number(m_Controls->m_StatisticsTable->item(7, 0)->data(Qt::DisplayRole).toDouble()); for(int z=0;zsetText(clipboard, QClipboard::Clipboard); } else { QApplication::clipboard()->clear(); } QLocale::setDefault(tempLocal); } void QmitkImageStatisticsView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*part*/, const QList &nodes ) { if (this->m_Visible) { this->SelectionChanged( nodes ); } else { this->m_DataNodeSelectionChanged = true; } } void QmitkImageStatisticsView::SelectionChanged(const QList &selectedNodes) { //Clear Histogram if data node is deselected m_Controls->m_JSHistogram->Clear(); if( this->m_StatisticsUpdatePending ) { this->m_DataNodeSelectionChanged = true; return; // not ready for new data now! } if (selectedNodes.size() == this->m_SelectedDataNodes.size()) { int i = 0; for (; i < selectedNodes.size(); ++i) { if (selectedNodes.at(i) != this->m_SelectedDataNodes.at(i)) { break; } } // node selection did not change if (i == selectedNodes.size()) return; } //reset the feature image and image mask field m_Controls->m_SelectedFeatureImageLabel->setText("None"); m_Controls->m_SelectedMaskLabel->setText("None"); this->ReinitData(); if (selectedNodes.isEmpty()) { - m_Controls->m_lineRadioButton->setEnabled(true); - m_Controls->m_barRadioButton->setEnabled(true); - m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true); - m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true); - m_Controls->m_UseDefaultBinSizeBox->setEnabled(true); - - m_Controls->m_InfoLabel->setText(""); - m_Controls->groupBox->setEnabled(false); - m_Controls->groupBox_3->setEnabled(false); + DisableHistogramGUIElements(); } else { - m_Controls->groupBox->setEnabled(true); - m_Controls->groupBox_3->setEnabled(true); - m_Controls->m_barRadioButton->setChecked(true); + EnableHistogramGUIElements(); + ResetHistogramGUIElementsToDefault(); } if(selectedNodes.size() == 1 || selectedNodes.size() == 2) { bool isBinary = false; selectedNodes.value(0)->GetBoolProperty("binary",isBinary); mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); isBinary |= isLabelSet->CheckNode(selectedNodes.value(0)); if(isBinary) { - m_Controls->m_lineRadioButton->setEnabled(true); - m_Controls->m_barRadioButton->setEnabled(true); - m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true); - m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true); - m_Controls->m_UseDefaultBinSizeBox->setEnabled(true); + EnableHistogramGUIElements(); m_Controls->m_InfoLabel->setText(""); } for (int i= 0; i< selectedNodes.size(); ++i) { this->m_SelectedDataNodes.push_back(selectedNodes.at(i)); } this->m_DataNodeSelectionChanged = false; this->m_Controls->m_ErrorMessageLabel->setText( "" ); this->m_Controls->m_ErrorMessageLabel->hide(); emit StatisticsUpdate(); } else { this->m_DataNodeSelectionChanged = false; } } +void QmitkImageStatisticsView::DisableHistogramGUIElements() +{ + m_Controls->m_InfoLabel->setText(""); + m_Controls->groupBox_histogram->setEnabled(false); + m_Controls->groupBox_statistics->setEnabled(false); +} + +void QmitkImageStatisticsView::ResetHistogramGUIElementsToDefault() +{ + m_Controls->m_barRadioButton->setChecked(true); + m_Controls->m_HistogramNBinsSpinbox->setValue(100); + m_HistogramNBins = m_Controls->m_HistogramNBinsSpinbox->value(); + m_Controls->m_UseDefaultNBinsCheckBox->setChecked(true); + m_Controls->m_ShowSubchartCheckBox->setChecked(true); + m_Controls->m_BinSizeFrame->setEnabled(false); + m_Controls->m_barRadioButton->setEnabled(true); + m_Controls->m_lineRadioButton->setEnabled(true); + m_Controls->m_HistogramNBinsSpinbox->setEnabled(true); + this->m_CalculationThread->SetHistogramNBins(m_Controls->m_HistogramNBinsSpinbox->value()); +} + +void QmitkImageStatisticsView::EnableHistogramGUIElements() +{ + m_Controls->groupBox_histogram->setEnabled(true); + m_Controls->groupBox_plot->setEnabled(true); + m_Controls->groupBox_statistics->setEnabled(true); +} + void QmitkImageStatisticsView::ReinitData() { while( this->m_CalculationThread->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } if(this->m_SelectedImage != nullptr) { this->m_SelectedImage->RemoveObserver( this->m_ImageObserverTag); this->m_SelectedImage = nullptr; } if(this->m_SelectedImageMask != nullptr) { this->m_SelectedImageMask->RemoveObserver( this->m_ImageMaskObserverTag); this->m_SelectedImageMask = nullptr; } if(this->m_SelectedPlanarFigure != nullptr) { this->m_SelectedPlanarFigure->RemoveObserver( this->m_PlanarFigureObserverTag); this->m_SelectedPlanarFigure = nullptr; } this->m_SelectedDataNodes.clear(); this->m_StatisticsUpdatePending = false; m_Controls->m_ErrorMessageLabel->setText( "" ); m_Controls->m_ErrorMessageLabel->hide(); this->InvalidateStatisticsTableView(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); } void QmitkImageStatisticsView::OnThreadedStatisticsCalculationEnds() { - std::stringstream message; - message << ""; - m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->setText(""); m_Controls->m_ErrorMessageLabel->hide(); this->WriteStatisticsToGUI(); } void QmitkImageStatisticsView::UpdateStatistics() { mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); if ( renderPart == nullptr ) { this->m_StatisticsUpdatePending = false; return; } m_WorldMinList.clear(); m_WorldMaxList.clear(); // classify selected nodes mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateOr::Pointer imagePredicate = mitk::NodePredicateOr::New(isImage, isLabelSet); std::string maskName; std::string maskType; std::string featureImageName; unsigned int maskDimension = 0; // reset data from last run ITKCommandType::Pointer changeListener = ITKCommandType::New(); changeListener->SetCallbackFunction( this, &QmitkImageStatisticsView::SelectedDataModified ); mitk::DataNode::Pointer planarFigureNode; for( int i= 0 ; i < this->m_SelectedDataNodes.size(); ++i) { mitk::PlanarFigure::Pointer planarFig = dynamic_cast(this->m_SelectedDataNodes.at(i)->GetData()); if( imagePredicate->CheckNode(this->m_SelectedDataNodes.at(i)) ) { bool isMask = false; this->m_SelectedDataNodes.at(i)->GetPropertyValue("binary", isMask); isMask |= isLabelSet->CheckNode(this->m_SelectedDataNodes.at(i)); if( this->m_SelectedImageMask == nullptr && isMask) { this->m_SelectedImageMask = dynamic_cast(this->m_SelectedDataNodes.at(i)->GetData()); this->m_ImageMaskObserverTag = this->m_SelectedImageMask->AddObserver(itk::ModifiedEvent(), changeListener); maskName = this->m_SelectedDataNodes.at(i)->GetName(); maskType = m_SelectedImageMask->GetNameOfClass(); maskDimension = 3; } else if( !isMask ) { if(this->m_SelectedImage == nullptr) { this->m_SelectedImage = static_cast(this->m_SelectedDataNodes.at(i)->GetData()); this->m_ImageObserverTag = this->m_SelectedImage->AddObserver(itk::ModifiedEvent(), changeListener); } featureImageName = this->m_SelectedDataNodes.at(i)->GetName(); } } else if (planarFig.IsNotNull()) { if(this->m_SelectedPlanarFigure == nullptr) { this->m_SelectedPlanarFigure = planarFig; this->m_PlanarFigureObserverTag = this->m_SelectedPlanarFigure->AddObserver(mitk::EndInteractionPlanarFigureEvent(), changeListener); maskName = this->m_SelectedDataNodes.at(i)->GetName(); maskType = this->m_SelectedPlanarFigure->GetNameOfClass(); maskDimension = 2; planarFigureNode = m_SelectedDataNodes.at(i); } } else { - std::stringstream message; - message << "" << "Invalid data node type!" << ""; - m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->setText("Invalid data node type!"); m_Controls->m_ErrorMessageLabel->show(); } } if(maskName == "") { maskName = "None"; maskType = ""; maskDimension = 0; } if(featureImageName == "") { featureImageName = "None"; } if (m_SelectedPlanarFigure != nullptr && m_SelectedImage == nullptr) { mitk::DataStorage::SetOfObjects::ConstPointer parentSet = this->GetDataStorage()->GetSources(planarFigureNode); for (unsigned int i=0; iSize(); i++) { mitk::DataNode::Pointer node = parentSet->ElementAt(i); if( imagePredicate->CheckNode(node) ) { bool isMask = false; node->GetPropertyValue("binary", isMask); isMask |= isLabelSet->CheckNode(node); if( !isMask ) { if(this->m_SelectedImage == nullptr) { this->m_SelectedImage = static_cast(node->GetData()); this->m_ImageObserverTag = this->m_SelectedImage->AddObserver(itk::ModifiedEvent(), changeListener); } } } } } unsigned int timeStep = renderPart->GetTimeNavigationController()->GetTime()->GetPos(); if ( m_SelectedImage != nullptr && m_SelectedImage->IsInitialized()) { // Check if a the selected image is a multi-channel image. If yes, statistics // cannot be calculated currently. if ( m_SelectedImage->GetPixelType().GetNumberOfComponents() > 1 ) { - std::stringstream message; - message << "Multi-component images not supported."; - m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->setText( "Multi-component images not supported." ); m_Controls->m_ErrorMessageLabel->show(); this->InvalidateStatisticsTableView(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_CurrentStatisticsValid = false; this->m_StatisticsUpdatePending = false; - m_Controls->m_lineRadioButton->setEnabled(true); - m_Controls->m_barRadioButton->setEnabled(true); - m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true); - m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true); - m_Controls->m_UseDefaultBinSizeBox->setEnabled(true); + this->DisableHistogramGUIElements(); m_Controls->m_InfoLabel->setText(""); return; } std::stringstream maskLabel; maskLabel << maskName; if ( maskDimension > 0 ) { maskLabel << " [" << maskDimension << "D " << maskType << "]"; } m_Controls->m_SelectedMaskLabel->setText( maskLabel.str().c_str() ); m_Controls->m_SelectedFeatureImageLabel->setText(featureImageName.c_str()); // check time step validity if(m_SelectedImage->GetDimension() <= 3 && timeStep > m_SelectedImage->GetDimension(3)-1) { timeStep = m_SelectedImage->GetDimension(3)-1; } // Add the used mask time step to the mask label so the user knows which mask time step was used // if the image time step is bigger than the total number of mask time steps (see // ImageStatisticsCalculator::ExtractImageAndMask) if (m_SelectedImageMask != nullptr) { unsigned int maskTimeStep = timeStep; if (maskTimeStep >= m_SelectedImageMask->GetTimeSteps()) { maskTimeStep = m_SelectedImageMask->GetTimeSteps() - 1; } m_Controls->m_SelectedMaskLabel->setText(m_Controls->m_SelectedMaskLabel->text() + QString(" (t=") + QString::number(maskTimeStep) + QString(")")); } // check if the segmentation mask is empty if (m_SelectedImageMask != NULL) { typedef itk::Image ItkImageType; typedef itk::ImageRegionConstIteratorWithIndex< ItkImageType > IteratorType; ItkImageType::Pointer itkImage; mitk::CastToItkImage( m_SelectedImageMask, itkImage ); bool empty = true; IteratorType it( itkImage, itkImage->GetLargestPossibleRegion() ); while ( !it.IsAtEnd() ) { ItkImageType::ValueType val = it.Get(); if ( val != 0 ) { empty = false; break; } ++it; } if ( empty ) { - std::stringstream message; - message << "Empty segmentation mask selected..."; - m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->setText( "Empty segmentation mask selected..." ); m_Controls->m_ErrorMessageLabel->show(); return; } } //// initialize thread and trigger it this->m_CalculationThread->SetIgnoreZeroValueVoxel( m_Controls->m_IgnoreZerosCheckbox->isChecked() ); this->m_CalculationThread->Initialize( m_SelectedImage, m_SelectedImageMask, m_SelectedPlanarFigure ); this->m_CalculationThread->SetTimeStep( timeStep ); - std::stringstream message; - message << "Calculating statistics..."; - m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->setText("Calculating statistics..."); m_Controls->m_ErrorMessageLabel->show(); try { // Compute statistics - // this->m_CalculationThread->SetUseDefaultBinSize(m_Controls->m_UseDefaultBinSizeBox->isChecked()); this->m_CalculationThread->start(); } catch ( const mitk::Exception& e) { - std::stringstream message; - message << "" << e.GetDescription() << ""; - m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->setText("" + QString(e.GetDescription()) + ""); m_Controls->m_ErrorMessageLabel->show(); this->m_StatisticsUpdatePending = false; } catch ( const std::runtime_error &e ) { // In case of exception, print error message on GUI - std::stringstream message; - message << "" << e.what() << ""; - m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->setText("" + QString(e.what()) + ""); m_Controls->m_ErrorMessageLabel->show(); this->m_StatisticsUpdatePending = false; } 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->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->setText("" + QString(e.what()) + ""); m_Controls->m_ErrorMessageLabel->show(); this->m_StatisticsUpdatePending = false; } - AdaptBinSizeCheckboxStepsize(m_SelectedImage); } else { this->m_StatisticsUpdatePending = false; } } void QmitkImageStatisticsView::SelectedDataModified() { if( !m_StatisticsUpdatePending ) { emit StatisticsUpdate(); } } void QmitkImageStatisticsView::NodeRemoved(const mitk::DataNode *node) { while(this->m_CalculationThread->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } if (node->GetData() == m_SelectedImage) { m_SelectedImage = nullptr; } } void QmitkImageStatisticsView::RequestStatisticsUpdate() { if ( !m_StatisticsUpdatePending ) { if(this->m_DataNodeSelectionChanged) { this->SelectionChanged(this->GetCurrentSelection()); } else { this->m_StatisticsUpdatePending = true; this->UpdateStatistics(); } } if (this->GetRenderWindowPart()) this->GetRenderWindowPart()->RequestUpdate(); } -void QmitkImageStatisticsView::OnHistogramBinSizeBoxValueChanged() +void QmitkImageStatisticsView::OnHistogramNBinsCheckBoxValueChanged() { - if (m_Controls->m_HistogramBinSizeSpinbox->value() != m_HistogramBinSize) + if (static_cast(m_Controls->m_HistogramNBinsSpinbox->value()) != m_HistogramNBins) { - m_HistogramBinSize = m_Controls->m_HistogramBinSizeSpinbox->value(); - this->m_CalculationThread->SetHistogramBinSize(m_Controls->m_HistogramBinSizeSpinbox->value()); + m_HistogramNBins = m_Controls->m_HistogramNBinsSpinbox->value(); + this->m_CalculationThread->SetHistogramNBins(m_Controls->m_HistogramNBinsSpinbox->value()); this->UpdateStatistics(); } } void QmitkImageStatisticsView::WriteStatisticsToGUI() { m_Controls->m_JSHistogram->Clear(); //Disconnect OnLineRadioButtonSelected() to prevent reloading chart when radiobutton is checked programmatically disconnect((QObject*)(this->m_Controls->m_JSHistogram), SIGNAL(PageSuccessfullyLoaded()), 0, 0); connect((QObject*)(this->m_Controls->m_JSHistogram), SIGNAL(PageSuccessfullyLoaded()), (QObject*) this, SLOT(OnPageSuccessfullyLoaded())); - m_Controls->m_lineRadioButton->setEnabled(true); - m_Controls->m_barRadioButton->setEnabled(true); - m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true); - m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true); m_Controls->m_InfoLabel->setText(""); if (m_DataNodeSelectionChanged) { this->m_StatisticsUpdatePending = false; this->RequestStatisticsUpdate(); return; // stop visualization of results and calculate statistics of new selection } if (this->m_CalculationThread->GetStatisticsUpdateSuccessFlag()) { if (this->m_CalculationThread->GetStatisticsChangedFlag()) { // Do not show any error messages m_Controls->m_ErrorMessageLabel->hide(); m_CurrentStatisticsValid = true; } if (m_SelectedImage != nullptr) { //all statistics are now computed also on planar figures (lines, paths...)! // If a (non-closed) PlanarFigure is selected, display a line profile widget if (m_SelectedPlanarFigure != nullptr) { // Check if the (closed) planar figure is out of bounds and so no image mask could be calculated--> Intensity Profile can not be calculated bool outOfBounds = false; if (m_SelectedPlanarFigure->IsClosed() && m_SelectedImageMask == nullptr) { outOfBounds = true; const QString message("Planar figure is on a rotated image plane or outside the image bounds."); m_Controls->m_InfoLabel->setText(message); } // check whether PlanarFigure is initialized const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_SelectedPlanarFigure->GetPlaneGeometry(); if (!(planarFigurePlaneGeometry == nullptr || outOfBounds)) { unsigned int timeStep = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->GetPos(); mitk::Image::Pointer image; if (this->m_CalculationThread->GetStatisticsImage()->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(this->m_CalculationThread->GetStatisticsImage()); timeSelector->SetTimeNr(timeStep); timeSelector->Update(); image = timeSelector->GetOutput(); } else { image = this->m_CalculationThread->GetStatisticsImage(); } mitk::IntensityProfile::ConstPointer intensityProfile = (mitk::IntensityProfile::ConstPointer)mitk::ComputeIntensityProfile(image, m_SelectedPlanarFigure); auto intensityProfileList = ConvertIntensityProfileToVector(intensityProfile); auto lineDataLabel = "Intensity profile " + m_Controls->m_SelectedMaskLabel->text().toStdString(); m_Controls->m_JSHistogram->SetChartType(lineDataLabel, QmitkChartWidget::ChartType::line); m_Controls->m_JSHistogram->AddData1D(intensityProfileList, lineDataLabel); m_Controls->m_JSHistogram->SetXAxisLabel("Distance"); m_Controls->m_JSHistogram->SetYAxisLabel("Intensity"); m_Controls->m_JSHistogram->Show(m_Controls->m_ShowSubchartCheckBox->isChecked()); m_Controls->m_lineRadioButton->setChecked(true); m_Controls->m_lineRadioButton->setEnabled(false); m_Controls->m_barRadioButton->setEnabled(false); - m_Controls->m_HistogramBinSizeSpinbox->setEnabled(false); - m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(false); - m_Controls->m_UseDefaultBinSizeBox->setEnabled(false); + m_Controls->m_HistogramNBinsSpinbox->setEnabled(false); + m_Controls->m_BinSizeFrame->setEnabled(false); + m_Controls->m_UseDefaultNBinsCheckBox->setEnabled(false); //Reconnect OnLineRadioButtonSelected() connect((QObject*)(this->m_Controls->m_JSHistogram), SIGNAL(PageSuccessfullyLoaded()), (QObject*) this, SLOT(OnLineRadioButtonSelected())); auto statisticsVector = this->m_CalculationThread->GetStatisticsData(); //only one entry (current timestep) this->FillLinearProfileStatisticsTableView(statisticsVector.front().GetPointer(), this->m_CalculationThread->GetStatisticsImage()); QString message("Only linegraph available for an intensity profile!"); if (this->m_CalculationThread->GetStatisticsImage()->GetDimension() == 4) { message += "Only current timestep displayed!"; } message += ""; m_Controls->m_InfoLabel->setText(message); m_CurrentStatisticsValid = true; } else { // Clear statistics, histogram, and GUI this->InvalidateStatisticsTableView(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex(0); m_CurrentStatisticsValid = false; m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_SelectedMaskLabel->setText("None"); this->m_StatisticsUpdatePending = false; - m_Controls->m_lineRadioButton->setEnabled(true); - m_Controls->m_barRadioButton->setEnabled(true); - m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true); - m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true); if (!outOfBounds) m_Controls->m_InfoLabel->setText(""); return; } } else { m_Controls->m_StatisticsWidgetStack->setCurrentIndex(0); - m_Controls->m_HistogramBinSizeSpinbox->setValue(this->m_CalculationThread->GetHistogramBinSize()); auto histogram = this->m_CalculationThread->GetTimeStepHistogram(this->m_CalculationThread->GetTimeStep()).GetPointer(); auto imageLabelName = m_Controls->m_SelectedFeatureImageLabel->text().toStdString(); m_Controls->m_JSHistogram->AddData2D(ConvertHistogramToMap(histogram), imageLabelName); m_Controls->m_JSHistogram->SetChartType(imageLabelName, QmitkChartWidget::ChartType::bar); this->m_Controls->m_JSHistogram->SetXAxisLabel("Gray value"); this->m_Controls->m_JSHistogram->SetYAxisLabel("Frequency"); - m_Controls->m_UseDefaultBinSizeBox->setEnabled(true); + m_Controls->m_UseDefaultNBinsCheckBox->setEnabled(true); m_Controls->m_JSHistogram->Show(this->m_Controls->m_ShowSubchartCheckBox->isChecked()); - auto currentTime = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->GetPos(); - this->AdaptBinSizeCheckBoxMinMax((this->m_CalculationThread->GetStatisticsData()).at(currentTime).GetPointer(), this->m_CalculationThread->GetStatisticsImage()->GetPixelType().GetComponentType()); this->FillStatisticsTableView(this->m_CalculationThread->GetStatisticsData(), this->m_CalculationThread->GetStatisticsImage()); } m_CurrentStatisticsValid = true; } } else { m_Controls->m_SelectedMaskLabel->setText("None"); m_Controls->m_ErrorMessageLabel->setText(m_CalculationThread->GetLastErrorMessage().c_str()); m_Controls->m_ErrorMessageLabel->show(); // Clear statistics and histogram this->InvalidateStatisticsTableView(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex(0); m_CurrentStatisticsValid = false; } berry::IPreferencesService* prefService = berry::WorkbenchPlugin::GetDefault()->GetPreferencesService(); m_StylePref = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE); this->m_StatisticsUpdatePending = false; } void QmitkImageStatisticsView::FillStatisticsTableView( const std::vector &statistics, const mitk::Image *image ) { this->m_Controls->m_StatisticsTable->setColumnCount(image->GetTimeSteps()); this->m_Controls->m_StatisticsTable->horizontalHeader()->setVisible(image->GetTimeSteps() > 1); // Set Checkbox for complete copy of statistic table if(image->GetTimeSteps()>1) { this->m_Controls->m_CheckBox4dCompleteTable->setEnabled(true); } else { this->m_Controls->m_CheckBox4dCompleteTable->setEnabled(false); this->m_Controls->m_CheckBox4dCompleteTable->setChecked(false); } for (unsigned int t = 0; t < image->GetTimeSteps(); t++) { this->m_Controls->m_StatisticsTable->setHorizontalHeaderItem(t, new QTableWidgetItem(QString::number(t))); if (statistics.at(t)->GetMaxIndex().size()==3) { mitk::Point3D index, max, min; index[0] = statistics.at(t)->GetMaxIndex()[0]; index[1] = statistics.at(t)->GetMaxIndex()[1]; index[2] = statistics.at(t)->GetMaxIndex()[2]; m_SelectedImage->GetGeometry()->IndexToWorld(index, max); this->m_WorldMaxList.push_back(max); index[0] = statistics.at(t)->GetMinIndex()[0]; index[1] = statistics.at(t)->GetMinIndex()[1]; index[2] = statistics.at(t)->GetMinIndex()[2]; m_SelectedImage->GetGeometry()->IndexToWorld(index, min); this->m_WorldMinList.push_back(min); } auto statisticsVector = AssembleStatisticsIntoVector(statistics.at(t).GetPointer(), image); unsigned int count = 0; for (const auto& entry : statisticsVector) { auto item = new QTableWidgetItem(entry); this->m_Controls->m_StatisticsTable->setItem(count, t, item); count++; } } this->m_Controls->m_StatisticsTable->resizeColumnsToContents(); int height = STAT_TABLE_BASE_HEIGHT; if (this->m_Controls->m_StatisticsTable->horizontalHeader()->isVisible()) height += this->m_Controls->m_StatisticsTable->horizontalHeader()->height(); if (this->m_Controls->m_StatisticsTable->horizontalScrollBar()->isVisible()) height += this->m_Controls->m_StatisticsTable->horizontalScrollBar()->height(); this->m_Controls->m_StatisticsTable->setMinimumHeight(height); // make sure the current timestep's column is highlighted (and the correct histogram is displayed) unsigned int t = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()-> GetPos(); mitk::SliceNavigationController::GeometryTimeEvent timeEvent(this->m_SelectedImage->GetTimeGeometry(), t); this->OnTimeChanged(timeEvent); t = std::min(image->GetTimeSteps() - 1, t); // See bug 18340 /*QString hotspotMean; hotspotMean.append(QString("%1").arg(s[t].GetHotspotStatistics().GetMean(), 0, 'f', decimals)); hotspotMean += " ("; for (int i=0; im_Controls->m_StatisticsTable->setItem( 7, t, new QTableWidgetItem( hotspotMean ) ); QString hotspotMax; hotspotMax.append(QString("%1").arg(s[t].GetHotspotStatistics().GetMax(), 0, 'f', decimals)); hotspotMax += " ("; for (int i=0; im_Controls->m_StatisticsTable->setItem( 8, t, new QTableWidgetItem( hotspotMax ) ); QString hotspotMin; hotspotMin.append(QString("%1").arg(s[t].GetHotspotStatistics().GetMin(), 0, 'f', decimals)); hotspotMin += " ("; for (int i=0; im_Controls->m_StatisticsTable->setItem( 9, t, new QTableWidgetItem( hotspotMin ) );*/ } std::vector QmitkImageStatisticsView::AssembleStatisticsIntoVector(mitk::ImageStatisticsCalculator::StatisticsContainer::ConstPointer statistics, mitk::Image::ConstPointer image, bool noVolumeDefined) const { std::vector result; unsigned int decimals = 2; //statistics of higher order should have 5 decimal places because they used to be very small unsigned int decimalsHigherOrderStatistics = 5; if (image->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE || image->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT) { decimals = 5; } result.push_back(GetFormattedString(statistics->GetMean(), decimals)); result.push_back(GetFormattedString(statistics->GetMedian(), decimals)); result.push_back(GetFormattedString(statistics->GetStd(), decimals)); result.push_back(GetFormattedString(statistics->GetRMS(), decimals)); result.push_back(GetFormattedString(statistics->GetMax(), decimals) + " " + GetFormattedIndex(statistics->GetMaxIndex())); result.push_back(GetFormattedString(statistics->GetMin(), decimals) + " " + GetFormattedIndex(statistics->GetMinIndex())); //to prevent large negative values of empty image statistics if (statistics->GetN() != std::numeric_limits::min()) { result.push_back(GetFormattedString(statistics->GetN(), 0)); const mitk::BaseGeometry *geometry = image->GetGeometry(); if (geometry != NULL && !noVolumeDefined) { const mitk::Vector3D &spacing = image->GetGeometry()->GetSpacing(); double volume = spacing[0] * spacing[1] * spacing[2] * static_cast(statistics->GetN()); result.push_back(GetFormattedString(volume, decimals)); } else { result.push_back("NA"); } } else { result.push_back("NA"); result.push_back("NA"); } result.push_back(GetFormattedString(statistics->GetSkewness(), decimalsHigherOrderStatistics)); result.push_back(GetFormattedString(statistics->GetKurtosis(), decimalsHigherOrderStatistics)); result.push_back(GetFormattedString(statistics->GetUniformity(), decimalsHigherOrderStatistics)); result.push_back(GetFormattedString(statistics->GetEntropy(), decimalsHigherOrderStatistics)); result.push_back(GetFormattedString(statistics->GetMPP(), decimals)); result.push_back(GetFormattedString(statistics->GetUPP(), decimalsHigherOrderStatistics)); return result; } void QmitkImageStatisticsView::FillLinearProfileStatisticsTableView(mitk::ImageStatisticsCalculator::StatisticsContainer::ConstPointer statistics, const mitk::Image *image) { this->m_Controls->m_StatisticsTable->setColumnCount(1); this->m_Controls->m_StatisticsTable->horizontalHeader()->setVisible(false); m_PlanarFigureStatistics = this->AssembleStatisticsIntoVector(statistics, image, true); for (unsigned int i = 0; i< m_PlanarFigureStatistics.size(); i++) { this->m_Controls->m_StatisticsTable->setItem( i, 0, new QTableWidgetItem(m_PlanarFigureStatistics[i] )); } this->m_Controls->m_StatisticsTable->resizeColumnsToContents(); int height = STAT_TABLE_BASE_HEIGHT; if (this->m_Controls->m_StatisticsTable->horizontalHeader()->isVisible()) height += this->m_Controls->m_StatisticsTable->horizontalHeader()->height(); if (this->m_Controls->m_StatisticsTable->horizontalScrollBar()->isVisible()) height += this->m_Controls->m_StatisticsTable->horizontalScrollBar()->height(); this->m_Controls->m_StatisticsTable->setMinimumHeight(height); } void QmitkImageStatisticsView::InvalidateStatisticsTableView() { this->m_Controls->m_StatisticsTable->horizontalHeader()->setVisible(false); this->m_Controls->m_StatisticsTable->setColumnCount(1); for ( int i = 0; i < this->m_Controls->m_StatisticsTable->rowCount(); ++i ) { { this->m_Controls->m_StatisticsTable->setItem( i, 0, new QTableWidgetItem( "NA" ) ); } } this->m_Controls->m_StatisticsTable->setMinimumHeight(STAT_TABLE_BASE_HEIGHT); } void QmitkImageStatisticsView::Activated() { } void QmitkImageStatisticsView::Deactivated() { } void QmitkImageStatisticsView::Visible() { m_Visible = true; mitk::IRenderWindowPart* renderWindow = GetRenderWindowPart(); if (renderWindow) { itk::ReceptorMemberCommand::Pointer cmdTimeEvent = itk::ReceptorMemberCommand::New(); cmdTimeEvent->SetCallbackFunction(this, &QmitkImageStatisticsView::OnTimeChanged); // It is sufficient to add the observer to the axial render window since the GeometryTimeEvent // is always triggered by all views. m_TimeObserverTag = renderWindow->GetQmitkRenderWindow("axial")-> GetSliceNavigationController()-> AddObserver(mitk::SliceNavigationController::GeometryTimeEvent(nullptr, 0), cmdTimeEvent); } if (m_DataNodeSelectionChanged) { if (this->IsCurrentSelectionValid()) { this->SelectionChanged(this->GetCurrentSelection()); } else { this->SelectionChanged(this->GetDataManagerSelection()); } m_DataNodeSelectionChanged = false; } } void QmitkImageStatisticsView::Hidden() { m_Visible = false; // The slice navigation controller observer is removed here instead of in the destructor. // If it was called in the destructor, the application would freeze because the view's // destructor gets called after the render windows have been destructed. if ( m_TimeObserverTag != 0 ) { mitk::IRenderWindowPart* renderWindow = GetRenderWindowPart(); if (renderWindow) { renderWindow->GetQmitkRenderWindow("axial")->GetSliceNavigationController()-> RemoveObserver( m_TimeObserverTag ); } m_TimeObserverTag = 0; } } void QmitkImageStatisticsView::SetFocus() { } -void QmitkImageStatisticsView::AdaptBinSizeCheckboxStepsize(mitk::Image::ConstPointer image) -{ - auto componentType = image->GetPixelType().GetComponentType(); - if (componentType == itk::ImageIOBase::DOUBLE || componentType == itk::ImageIOBase::FLOAT) { - m_Controls->m_HistogramBinSizeSpinbox->setDecimals(2); - m_Controls->m_HistogramBinSizeSpinbox->setSingleStep(.01); - } - else { - m_Controls->m_HistogramBinSizeSpinbox->setDecimals(0); - m_Controls->m_HistogramBinSizeSpinbox->setSingleStep(1); - } -} - -void QmitkImageStatisticsView::AdaptBinSizeCheckBoxMinMax(mitk::ImageStatisticsCalculator::StatisticsContainer::ConstPointer statistics, int componentType) -{ - auto minValue = statistics->GetMin(); - auto maxValue = statistics->GetMax(); - //10 bins are minimum as defined in ImageStatisticsCalculator::GetStatistics - const unsigned int minHistogramBins = 10; - //10000 bins are just a maximum arbitrary number to keep computation time reasonable - const unsigned int maxHistogramBins = 10000; - double minHistogramBinSize = static_cast((maxValue - minValue) / maxHistogramBins); - double maxHistogramBinSize = static_cast((maxValue - minValue) / minHistogramBins); - if (componentType == itk::ImageIOBase::DOUBLE || componentType == itk::ImageIOBase::FLOAT) { - //smallest value for double/float images - const double minHistogramBinSizeValid = 0.01; - m_Controls->m_HistogramBinSizeSpinbox->setMinimum(std::max(minHistogramBinSize, minHistogramBinSizeValid)); - m_Controls->m_HistogramBinSizeSpinbox->setMaximum(maxHistogramBinSize); - } - else { - //smallest valid value for int/short images - const double minHistogramBinSizeValid = 1; - m_Controls->m_HistogramBinSizeSpinbox->setMinimum(std::max(static_cast(minHistogramBinSize), static_cast(minHistogramBinSizeValid))); - m_Controls->m_HistogramBinSizeSpinbox->setMaximum(static_cast(maxHistogramBinSize)); - } -} - std::map QmitkImageStatisticsView::ConvertHistogramToMap(itk::Statistics::Histogram::ConstPointer histogram) const { std::map histogramMap; auto endIt = histogram->End(); auto it = histogram->Begin(); // generating Lists of measurement and frequencies for (; it != endIt; ++it) { double frequency = it.GetFrequency(); double measurement = it.GetMeasurementVector()[0]; histogramMap.emplace(measurement, frequency); } return histogramMap; } std::vector QmitkImageStatisticsView::ConvertIntensityProfileToVector(mitk::IntensityProfile::ConstPointer intensityProfile) const { std::vector intensityProfileList; auto end = intensityProfile->End(); for (auto it = intensityProfile->Begin(); it != end; ++it) { intensityProfileList.push_back(it.GetMeasurementVector()[0]); } return intensityProfileList; } QString QmitkImageStatisticsView::GetFormattedString(double value, unsigned int decimals) const { typedef mitk::ImageStatisticsCalculator::StatisticsContainer::RealType RealType; RealType maxVal = std::numeric_limits::max(); if (value == maxVal) { return QString("NA"); } else { return QString("%1").arg(value, 0, 'f', decimals); } } QString QmitkImageStatisticsView::GetFormattedIndex(const vnl_vector& vector) const { if (vector.empty()) { return QString(); } QString formattedIndex("("); for (const auto& entry : vector) { formattedIndex += QString::number(entry); formattedIndex += ","; } formattedIndex.chop(1); formattedIndex += ")"; return formattedIndex; } diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h index bbdbdb4faf..08246d6487 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h @@ -1,195 +1,190 @@ /*=================================================================== 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 QmitkImageStatisticsView_H__INCLUDED #define QmitkImageStatisticsView_H__INCLUDED #include "ui_QmitkImageStatisticsViewControls.h" // Qmitk includes #include -#include "QmitkStepperAdapter.h" +#include #include "QmitkImageStatisticsCalculationThread.h" #include // mitk includes -#include "mitkILifecycleAwarePart.h" -#include "mitkPlanarLine.h" +#include +#include #include #include /*! \brief QmitkImageStatisticsView is a bundle that allows statistics calculation from images. Three modes are supported: 1. Statistics of one image, 2. Statistics of an image and a segmentation, 3. Statistics -of an image and a Planar Figure. The statistics calculation is realized in a seperate thread to keep the -gui accessable during calculation. +of an image and a Planar Figure. The statistics calculation is realized in a separate thread to keep the +gui accessible during calculation. \ingroup Plugins/org.mitk.gui.qt.measurementtoolbox */ class QmitkImageStatisticsView : public QmitkAbstractView, public mitk::ILifecycleAwarePart, public berry::IPartListener { Q_OBJECT private: - /*! \ Convenient typedefs */ - typedef mitk::DataStorage::SetOfObjects ConstVector; - typedef ConstVector::ConstPointer ConstVectorPointer; - typedef ConstVector::ConstIterator ConstVectorIterator; - typedef std::map< mitk::Image *, mitk::ImageStatisticsCalculator::Pointer > ImageStatisticsMapType; typedef QList SelectedDataNodeVectorType; typedef itk::SimpleMemberCommand< QmitkImageStatisticsView > ITKCommandType; std::map ConvertHistogramToMap(itk::Statistics::Histogram::ConstPointer histogram) const; std::vector ConvertIntensityProfileToVector(mitk::IntensityProfile::ConstPointer intensityProfile) const; std::vector AssembleStatisticsIntoVector(mitk::ImageStatisticsCalculator::StatisticsContainer::ConstPointer statistics, mitk::Image::ConstPointer image, bool noVolumeDefined=false) const; QString GetFormattedIndex(const vnl_vector& vector) const; QString GetFormattedString(double value, unsigned int decimals) const; - void AdaptBinSizeCheckboxStepsize(mitk::Image::ConstPointer image); - void AdaptBinSizeCheckBoxMinMax(mitk::ImageStatisticsCalculator::StatisticsContainer::ConstPointer statistics, int componentType); public: /*! \brief default constructor */ QmitkImageStatisticsView(QObject *parent = nullptr, const char *name = nullptr); /*! \brief default destructor */ virtual ~QmitkImageStatisticsView(); /*! \brief method for creating the widget containing the application controls, like sliders, buttons etc. */ virtual void CreateQtPartControl(QWidget *parent) override; /*! \brief method for creating the connections of main and control widget */ virtual void CreateConnections(); /*! \brief Is called from the selection mechanism once the data manager selection has changed*/ void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList &selectedNodes) override; static const std::string VIEW_ID; static const int STAT_TABLE_BASE_HEIGHT; public slots: /** \brief Called when the statistics update is finished, sets the results to GUI.*/ void OnThreadedStatisticsCalculationEnds(); /** \brief Update bin size for histogram resolution. */ - void OnHistogramBinSizeBoxValueChanged(); + void OnHistogramNBinsCheckBoxValueChanged(); protected slots: /** \brief Saves the histogram to the clipboard */ void OnClipboardHistogramButtonClicked(); /** \brief Saves the statistics to the clipboard */ void OnClipboardStatisticsButtonClicked(); /** \brief Indicates if zeros should be excluded from statistics calculation */ void OnIgnoreZerosCheckboxClicked(); /** \brief Checks if update is possible and calls StatisticsUpdate() possible */ void RequestStatisticsUpdate(); /** \brief Jump to coordinates stored in the double clicked cell */ void JumpToCoordinates(int row, int col); /** \brief Toogle GUI elements if histogram default bin size checkbox value changed. */ - void OnDefaultBinSizeBoxChanged(); + void OnDefaultNBinsSpinBoxChanged(); void OnShowSubchartBoxChanged(); void OnBarRadioButtonSelected(); void OnLineRadioButtonSelected(); void OnPageSuccessfullyLoaded(); signals: /** \brief Method to set the data to the member and start the threaded statistics update */ void StatisticsUpdate(); protected: /** \brief Writes the calculated statistics to the GUI */ void FillStatisticsTableView(const std::vector &statistics, const mitk::Image *image); void FillLinearProfileStatisticsTableView(mitk::ImageStatisticsCalculator::StatisticsContainer::ConstPointer statistics, const mitk::Image *image); /** \brief Removes statistics from the GUI */ void InvalidateStatisticsTableView(); /** \brief Recalculate statistics for currently selected image and mask and * update the GUI. */ void UpdateStatistics(); virtual void Activated() override; virtual void Deactivated() override; virtual void Visible() override; virtual void Hidden() override; virtual void SetFocus() override; /** \brief Method called when itkModifiedEvent is called by selected data. */ void SelectedDataModified(); /** \brief Method called when the data manager selection changes */ void SelectionChanged(const QList &selectedNodes); + + void DisableHistogramGUIElements(); + + void ResetHistogramGUIElementsToDefault(); + + void EnableHistogramGUIElements(); + /** \brief Method called to remove old selection when a new selection is present */ void ReinitData(); /** \brief writes the statistics to the gui*/ void WriteStatisticsToGUI(); void NodeRemoved(const mitk::DataNode *node) override; /** \brief Is called right before the view closes (before the destructor) */ virtual void PartClosed(const berry::IWorkbenchPartReference::Pointer&) override; /** \brief Is called from the image navigator once the time step has changed */ void OnTimeChanged(const itk::EventObject&); /** \brief Required for berry::IPartListener */ virtual Events::Types GetPartEventTypes() const override { return Events::CLOSED; } // member variables Ui::QmitkImageStatisticsViewControls *m_Controls; // if you have a planar figure selected, the statistics values will be saved in this one. std::vector m_PlanarFigureStatistics; QmitkImageStatisticsCalculationThread* m_CalculationThread; - QmitkStepperAdapter* m_TimeStepperAdapter; - unsigned int m_CurrentTime; - QString m_Clipboard; - // Image and mask data mitk::Image* m_SelectedImage; mitk::Image* m_SelectedImageMask; mitk::PlanarFigure* m_SelectedPlanarFigure; // observer tags long m_ImageObserverTag; long m_ImageMaskObserverTag; long m_PlanarFigureObserverTag; long m_TimeObserverTag; SelectedDataNodeVectorType m_SelectedDataNodes; bool m_CurrentStatisticsValid; bool m_StatisticsUpdatePending; - bool m_StatisticsIntegrationPending; bool m_DataNodeSelectionChanged; bool m_Visible; - double m_HistogramBinSize; + unsigned int m_HistogramNBins; std::vector m_WorldMinList; std::vector m_WorldMaxList; berry::IPreferences::Pointer m_StylePref; }; #endif // QmitkImageStatisticsView_H__INCLUDED diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui index a3704b302a..7475b0623e 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui @@ -1,649 +1,649 @@ QmitkImageStatisticsViewControls true 0 0 548 812 Form 0 0 Qt::LeftToRight Feature Image: 0 0 None 0 0 Mask: 0 0 None -1 0 0 color: rgb(255, 0, 0); Error Message Qt::AutoText Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter Ignore zero-valued voxels - + false Statistics 9 9 9 100 180 16777215 16777215 Qt::ScrollBarAsNeeded Qt::ScrollBarAsNeeded true QAbstractItemView::NoEditTriggers true true Qt::DotLine false 14 1 false false 80 true 80 false true true false 25 25 false false Mean Median StdDev RMS Max Min N V (mm³) Skewness Kurtosis Uniformity Entropy MPP UPP 0 0 0 0 0 0 Copy to Clipboard Qt::Horizontal QSizePolicy::Fixed 20 20 false copy complete table Qt::Horizontal 40 20 - + false 400 450 Histogram false - + + + false + 0 0 0 0 16777215 16777215 Plot 0 0 Barchart true 0 0 0 0 Linegraph Qt::Horizontal 40 20 Show Subchart true - + - Use default bin size + Use default #bins true QFrame::NoFrame QFrame::Raised 0 0 0 0 0 - + 60 0 100 16777215 - Bin size: + # bins: - + + + false + Press enter to recalculate statistics with new bin size. - - true - - - 2 - - 0.010000000000000 + 1 - 1000000.000000000000000 + 10000 - 100.000000000000000 + 100 0 0 0 0 0 0 0 0 0 0 0 0 Copy to Clipboard Qt::Horizontal 40 20 Qt::Vertical 20 40 QmitkChartWidget QWidget
QmitkChartWidget.h